`
cyxlgzs
  • 浏览: 89938 次
  • 性别: Icon_minigender_1
社区版块
存档分类
最新评论

NIO Socket实现文件服务器

 
阅读更多

一、背景

很多时候我们都会用FTP工具来实现文件的上传下载功能,于是琢磨着也用java的相关知识来自己实现一个简单的文件服务器

二、NIO Socket

考虑到文件的上传下载其实就是客户端和服务器进行通讯,然后进行数据交换。此时就可以采用Socket来实现。从JDK1.4版本以来java提供了更加高效的非阻塞形式的Socket,也就是NIO方式的Socket,通过通道Channel和缓冲器Buffer的方式来进行数据的读写。关于NIO的介绍,这里找了网上的一篇文章,可以参考一下 JAVA NIO简介

三、实施步骤

1、实现文件的上传下载等,需要服务器和客户端两部分。服务端我们取名为FileCenter,客户端我们取名为FileClient

2、首先实现FileCenter,具体代码如下,理解可参照注释

package org.filecenter;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;

/**
 * 文件下中心,命令模式如下 
 * 显示文件:list 
 * 		列出所有可下载的文件 
 * 下载文件:get server_file [local_file] 
 * 		server_file服务器中的文件名,local_file本地存储路径
 * 上传文件:put local_file [server_file] 
 * 		local_file本地文件路径,server_file服务器存储文件名
 * 
 * @author cyxl
 * 
 */
public class FileCenter {
	private static Selector selector; // 选择器
	private static final int server_port = 12345; // 服务器端口
	private static CharsetDecoder decoder = Charset.forName("GB2312")
			.newDecoder(); // 字节转字符
	private static CharsetEncoder encoder = Charset.forName("GB2312")
			.newEncoder(); // 字符转字节
	private static ByteBuffer buffer = ByteBuffer.allocate(1024);
	private static final String server_path = "C:\\file_center\\"; // 服务器文件路径

	public static void main(String[] args) {
		try {
			selector = Selector.open();// 打开选择器
			ServerSocketChannel serverChannel = ServerSocketChannel.open();
			ServerSocket server = serverChannel.socket();
			server.bind(new InetSocketAddress(server_port));
			serverChannel.configureBlocking(false);
			serverChannel.register(selector, SelectionKey.OP_ACCEPT);
			System.out.println("等待客户端连接……");
			while (true) {
				selector.select();
				Iterator<SelectionKey> itr = selector.selectedKeys().iterator();
				while (itr.hasNext()) {
					SelectionKey key = itr.next();
					itr.remove();
					process(key);
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	public static void process(SelectionKey key) throws IOException {
		if (key.isAcceptable()) {
			// 连接
			ServerSocketChannel serverChannel = (ServerSocketChannel) key
					.channel();
			SocketChannel client = serverChannel.accept();
			client.configureBlocking(false);
			SelectionKey sKey = client.register(selector, SelectionKey.OP_READ);
			sKey.attach("[r_cmd]"); // 连接好后读取客户端发来的命令
		} else if (key.isReadable()) {
			// 读取
			SocketChannel channel = (SocketChannel) key.channel();

			String attach = key.attachment().toString();
			if (attach.equals("[r_cmd]")) {
				// 获取命令
				int len = channel.read(buffer);
				
				if (len > 0) {
					buffer.flip();
					String cmd = "";
					CharBuffer charBuffer = decoder.decode(buffer);
					cmd = charBuffer.toString();
					
					SelectionKey sKey = channel.register(selector,
							SelectionKey.OP_WRITE);
					System.out.println("cmd:" + cmd);
					if (cmd.trim().equals("list")) {
						sKey.attach("[list_file]");
					} else {
						String[] temp = cmd.split(" ");
						if (temp.length >= 2) {
							cmd = temp[0];
							String filename = temp[1];

							if (cmd.equals("get")) {
								// 下载
								File file = new File(server_path, filename);
								if (file.exists()) {
									sKey.attach("[get]:" + filename);
								} else {
									sKey.attach("[no_file]");
								}
							} else if (cmd.equals("put")) {
								// 上传
								sKey.attach("[put]:" + filename);
							} else {
								// 错误命令格式
								sKey.attach("[error_command]");
							}
						}

					}
				} else {
					channel.close();
				}
			}
			buffer.clear();
		} else if (key.isWritable()) {
			// 写入
			SocketChannel channel = (SocketChannel) key.channel();
			String attach = key.attachment().toString();
			if (attach.startsWith("[list_file]")) {
				channel.write(encoder.encode(CharBuffer.wrap("list files")));
				File file = new File(server_path);
				String[] filenames = file.list();
				String temp = "";
				for (String filename : filenames) {
					temp += filename + ";";
				}
				temp = temp.substring(0, temp.length() - 1);
				// 写入所有可下载的文件
				channel.write(ByteBuffer.wrap(temp.getBytes()));

				channel.close();
			} else if (attach.equals("[no_file]")) {
				channel.write(ByteBuffer.wrap("no such file".getBytes()));

				channel.close();
			} else if (attach.equals("[error_command]")) {
				channel.write(ByteBuffer.wrap("error command".getBytes()));

				channel.close();
			} else if (attach.startsWith("[get]")) {
				channel.write(encoder.encode(CharBuffer.wrap("开始下载")));
				File file = new File(server_path, attach.split(":")[1]);
				DataInputStream dis = new DataInputStream(
						new BufferedInputStream(new FileInputStream(file)));

				int len = 0;
				byte[] buf = new byte[1024];
				while ((len = dis.read(buf)) != -1) {
					channel.write(ByteBuffer.wrap(buf, 0, len));
				}
				dis.close();
				System.out.println("下载完成");
				channel.close();
			} else if (attach.startsWith("[put]")) {
				channel.write(encoder.encode(CharBuffer.wrap("开始上传")));
				DataOutputStream dos = new DataOutputStream(
						new BufferedOutputStream(new FileOutputStream(new File(
								server_path, attach.split(":")[1]))));
				int len = channel.read(buffer);
				while (len >= 0) {
					if (len != 0) {
						buffer.flip();
					}
					dos.write(buffer.array(), 0, len);
					len = channel.read(buffer);
				}
				dos.close();
				channel.close();
				System.out.println("上传完毕");
			}
		}
	}

}

作为服务器,我们首先在C盘根目录下新建一个file_center的目录,该目录下的的所有文件可提供下载,上传的文件也是保存在这个目录下

3、客户端FileClient的实现代码如下

package org.filecenter;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.net.InetAddress;
import java.net.Socket;
import java.util.Scanner;

public class FileClient {

	private int ServerPort = 12345;
	private String ServerAddress = "127.0.0.1";
	private String CMD = "list";
	private String local_file = "";
	private String server_file = "";

	class SocketThread extends Thread {

		@Override
		public void run() {
			try {
				File file = new File(local_file); 
				if (!file.exists() && CMD.equals("put")) {
					System.out.println("本地没有这个文件,无法上传!");
					return;
				}

				InetAddress loalhost = InetAddress.getLocalHost();
				Socket socket = new Socket(ServerAddress, ServerPort, loalhost,
						44);
				// 服务器IP地址 端口号 本机IP 本机端口号
				DataInputStream dis = new DataInputStream(socket
						.getInputStream());
				DataOutputStream dos = new DataOutputStream(socket
						.getOutputStream());

				// dos.writeUTF(GetOrPut+" "+server_file);//服务器端如果是do的socket,writeUTF和writeUTF对接
				dos.write((CMD + " " + server_file).getBytes());
				dos.flush();

				// String tempString = dis.writeUTF();
				byte[] buf = new byte[1024];
				int len = dis.read(buf);
				String tempString = new String(buf, 0, len);// 服务器反馈的信息

				// System.out.println(tempString);
				if (tempString.equals("no such file")) {
					System.out.println("服务器没有这个文件,无法下载!");
					dos.close();
					dis.close();
					socket.close();
					return;
				}

				if (tempString.startsWith("开始下载")) {
					DataOutputStream fileOut = new DataOutputStream(
							new BufferedOutputStream(new FileOutputStream(file)));

					while ((len = dis.read(buf)) != -1) {
						fileOut.write(buf, 0, len);
					}
					System.out.println("下载完毕!");
					fileOut.close();
					dos.close();
					dis.close();
					socket.close();
				} else if (tempString.equals("开始上传")) {
					System.out.println("正在上传文件.......");
					DataInputStream fis = new DataInputStream(
							new BufferedInputStream(new FileInputStream(file)));

					while ((len = fis.read(buf)) != -1) {
						dos.write(buf, 0, len);
					}
					dos.flush();
					System.out.println("上传完毕!");
					fis.close();
					dis.close();
					dos.close();
					socket.close();
				}
				else if(tempString.equals("list files"))
				{
					len=dis.read(buf);
					String temp=new String(buf,0,len);
					String[] strs=temp.split(";");
					System.out.println("文件列表");
					for(String str:strs)
					{
						System.out.println(str);
					}
					dis.close();
					dos.close();
					socket.close();
				}

			} catch (Exception e) {
				e.printStackTrace();
			}
		}

	}

	public boolean checkCommand(String command) {
		if (!command.startsWith("put") && !command.startsWith("get") && !command.equals("list")) {
			System.out.println("输入命令错误");
			return false;
		}

		int index = -1;
		String temp = "";
		String[] tempStrings = null;

		if ((index = command.indexOf("-h")) > 0) {
			temp = command.substring(index + 3);
			temp = temp.substring(0, temp.indexOf(' '));
			ServerAddress = temp;
		}
		if ((index = command.indexOf("-p")) > 0) {
			temp = command.substring(index + 3);
			temp = temp.substring(0, temp.indexOf(' '));
			ServerPort = Integer.valueOf(temp);
		}

		tempStrings = command.split(" ");
		if (command.startsWith("put")) {
			CMD = "put";
			local_file = tempStrings[tempStrings.length - 2];
			server_file = tempStrings[tempStrings.length - 1];
		} else if (command.startsWith("get")) {
			CMD = "get";
			local_file = tempStrings[tempStrings.length - 1];
			server_file = tempStrings[tempStrings.length - 2];
		}
		else
		{
			CMD = "list";
		}

		return true;
	}

	public static void main(String[] args) {
		FileClient client = new FileClient();
		Scanner sc = new Scanner(System.in);
		String commandString = "";
		do {
			System.out.println("请输入命令:");
			commandString = sc.nextLine();
		} while (!client.checkCommand(commandString));

		FileClient.SocketThread a = client.new SocketThread();
		a.start();

	}
}

这里我们提供了三种命令方式

1)list命令,列出服务器中所有可供下载的文件列表

2)get命令,下载服务器中的文件。示例:get test.txt d:\test2.txt

3)put命令,上传文件至服务器。示例:put e:\hello.rar hello.rar

4、将上述两个java文件进行编译,然后开启两个命令窗口进行测试

四、总结

1、此文件服务器相对简单,有很多可以扩展和进行改善的地方。比如可以根据需要对文件的上传下载等功能可以开启独立的线程进行操作

2、在文件的读写方面也可以考虑nio的方式进行

版权声明:本文为博主原创文章,未经博主允许不得转载。

分享到:
评论

相关推荐

    java NIO socket聊天室

    使用NIO socket不需要多线程来处理多个连接的请求,效率非常高 可以作为NIO socket入门的例子,Reactor模式,重点理解key.attach, jar文件里包含了源代码 1,运行server.bat启动服务器,可以打开编辑,修改端口号 ...

    java解读NIOSocket非阻塞模式.zip

    jdk供的无阻塞I/O(NIO)有效解决了多线程服务器存在的线程开销问题,但在使用上略显得复杂一些。在NIO中使用多线程,主要目的已不是为了应对每个客户端请求而分配独立的服务线程,而是通过多线程充分使用用多个CPU...

    Java应用服务器Java-Nio-Services.zip

    JAVA服务器基于JAVA NIO I. 实现HTTP协议 II. 实现HTTPS协议 III. 实现FASTCGI协议(Client端) 运行HTTP/HTTPS服务器 运行开发包下jar文件 java -jar http-server-version-{version}.jar   ...

    Java视频教程 Java游戏服务器端开发 Netty NIO AIO Mina视频教程

    jaca视频教程 jaca游戏服务器端开发 Netty NIO AIO Mina视频教程 课程目录: 一、Netty快速入门教程 01、第一课NIO 02、第二课netty服务端 03、第三课netty客户端 04、第四课netty线程模型源码分析(一) 05、...

    JAVA多服务器通讯框架-聊天功能演示程序 V0.1 alpha

    JAVA多服务器通讯框架是基于NIO开发的Socket通讯框架,实现了客户端和服务器,服务器与服务器之间的通讯功能,适合应用于大型聊天服务器,大型游戏服务器。 本演示程序实现了一个基本的命令行聊天功能,以演示...

    Naga-Socket:Naga-Socket - Java 简单套接字

    Naga 的目标是成为一个非常小的 NIO 库,它提供了一些 java 类来用异步 NIO 对应物(类似于为 Java 1.7 计划的 NIO2)包装通常的 Socket 和 ServerSocket。 所有这些都是从单个线程驱动的,使其对客户端(例如,...

    基础深化和提高-网络编程

    通过Java的网络编程能力,开发人员可以创建客户端-服务器应用程序、实现数据传输、远程调用等功能。 Java的网络编程通常涉及以下几个方面: Socket编程:基于Socket套接字,通过TCP或UDP协议进行网络通信。通过...

    FileSync:FileSync是一个基于Java Socket的应用程序,用于使多个客户端上的文件保持同步。 它包含一个服务器和一个客户端应用程序。 多个客户端可以连接到服务器,并且此应用程序将自动同步其存储库

    文件同步FileSync是一个基于Java Socket的应用程序,用于使多个客户端上的文件保持同步。 它包含一个服务器和一个客户端应用程序。 多个客户端可以与服务器连接,并且此应用程序将自动同步其存储库。 该应用程序当前...

    Java网络编程(第3版) 中文版 文件2

    本书内容全面,涵盖了从网络基础知识到远程方法调用(RMI)等各方面的内容,书中章节涉及到TCP和UDP socket、服务器socket、URL和URI、组播以及特殊用途的API(如JavaMail)等等。本书展示了如何使用JSSE编写安全的...

    Java网络编程(第三版)中文版.part11.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)高清中文版.part01.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part06.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part07.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part09.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part01.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part03.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part02.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part04.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

    Java网络编程(第三版)中文版.part10.rar

    本书展示了如何使用JSSE编写安全的网络应用程序,解释了如何使用NIO API编写超高性能的服务器。它还涵盖了Java对网络代理、Web cookie和URL缓存的支持。 《Java网络编程》不仅仅是对API的解释:它还展示了如何使用...

Global site tag (gtag.js) - Google Analytics