传统的Java 的IO,利用Socket建立服务器,接收客户端连接,一般都是为每一个连接建立一个线程,如果连接数巨大,那么服务器开销也将巨大。。NIO的原理,可以参照图:http://new.51cto.com/files/uploadimg/20080912/150103487.jpg
Socket的Channel在Selector上注册某一种动作,Selector通过select操作,监视所有在该Selector注册过的Channel的对应的动作,如果监测到某一对应的动作,则返回selectedKeys,自己手动取到各个SelectionKey进行相应的处理。当然NIO不仅可以接受Socket的Channel,还有文件操作等其他IO操作。
作业的要求:
使用socket编程实现一个简单的文件服务器。客户端程序实现put功能(将一个文件从本地传到文件服务器)和get功能(从文件服务器取一远程文件存为本地文件)。客户端和文件服务器不在同一台机器上。
put [-h hostname] [-p portname] local_filename remote_filename
get [-h hostname] [-p portname] remote_filename local_filename
服务器端不使用nio,直接使用io的socket代码如下:
import java.io.*; import java.net.ServerSocket; import java.net.Socket; public class ServerMain { public static void main(String[] args) { class SocketThread extends Thread{ private Socket socket; private byte[] buf; private int len = 0; public SocketThread(Socket socket) { this.socket = socket; buf = new byte[1024]; } @Override public void run() { try { DataInputStream dis = new DataInputStream(socket.getInputStream()); DataOutputStream dos = new DataOutputStream(socket.getOutputStream()); //String command = dis.readUTF(); len = dis.read(buf); String command = new String(buf,0,len); System.out.println("command=="+command); String[] temp =command.split(" "); command = temp[0]; //命令 是put还是get String filename = temp[1]; //文件名 File file = new File("C:\\",filename);//假设放在C盘 if(command.equals("get")){ if(!file.exists()){ //dos.writeUTF("notexists"); dos.write("notexists".getBytes()); dos.flush(); System.out.println("没有这个文件,无法提供下载!"); dis.close(); dos.close(); socket.close(); return; } //dos.writeUTF("DownloadReady "+file.length()); dos.write("准备下载".getBytes()); dos.flush(); 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(); fis.close(); System.out.println("文件传输完成"); } else { //dos.writeUTF("UploadReady"); dos.write("UploadReady".getBytes()); dos.flush(); System.out.println("正在接受文件上传..."); 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(); } dis.close(); dos.close(); socket.close(); } catch (Exception e) { e.printStackTrace(); } } } System.out.println("等待客户端连接...."); int index = 0; try { ServerSocket server = new ServerSocket(9527,300); //端口号9527 允许最大连接数300 while (true) { Socket socket = server.accept(); System.out.println("收到第"+(++index)+"个连接"); new SocketThread(socket).start(); //对每个连接创建一个线程 } } catch (Exception e) { e.printStackTrace(); } } }
使用NIO建立的Socket服务器,代码如下:
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.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; public class NewSocketServer { private static final int port = 9527; private Selector selector; private ByteBuffer clientBuffer = ByteBuffer.allocate(1024); private CharsetDecoder decoder = Charset.forName("GB2312").newDecoder(); private CharsetEncoder encoder = Charset.forName("GB2312").newEncoder(); //编码解码格式设置成GBK也行.UTF-8不行,中文乱码 (前提都是客户端没有设置任何编码解码格式) public void setListener() throws Exception{ selector = Selector.open(); //打开选择器 ServerSocketChannel server = ServerSocketChannel.open(); //定义一个 ServerSocketChannel通道 server.socket().bind(new InetSocketAddress(port)); //ServerSocketChannel绑定端口 server.configureBlocking(false); //配置通道使用非阻塞模式 server.register(selector, SelectionKey.OP_ACCEPT); //该通道在selector上注册 接受连接的动作 while(true) { selector.select(); //select() 会阻塞,直到在该selector上注册的channel有对应的消息读入 Iterator iter = selector.selectedKeys().iterator(); while (iter.hasNext()) { SelectionKey key = (SelectionKey) iter.next(); iter.remove(); // 删除此消息 process(key); // 当前线程内处理。(为了高效,一般会在另一个线程中处理此消息) } } } private void process(SelectionKey key) throws IOException { if (key.isAcceptable()) { // 接收请求 ServerSocketChannel server = (ServerSocketChannel) key.channel(); SocketChannel channel = server.accept();//类似于io的socket,ServerSocketChannel的accept函数返回 SocketChannel channel.configureBlocking(false); //设置非阻塞模式 SelectionKey sKey = channel.register(selector, SelectionKey.OP_READ); sKey.attach("read_command"); //这儿接收到连接请求之后可以为每个连接设置一个ID } else if (key.isReadable()) { // 读信息 SocketChannel channel = (SocketChannel) key.channel(); String name = (String) key.attachment(); if(name.equals("read_command")){ int count = channel.read(clientBuffer); if (count > 0) { clientBuffer.flip(); CharBuffer charBuffer = decoder.decode(clientBuffer); String command = charBuffer.toString(); //command形如:get abc.png 或者 put aaa.png System.out.println("command===="+command); //得到客户端传来的命令 String[] temp =command.split(" "); command = temp[0]; //命令 是put还是get String filename = temp[1]; //文件名 SelectionKey sKey = channel.register(selector,SelectionKey.OP_WRITE); if(command.equals("put"))sKey.attach("UploadReady#"+filename); //要保护该通道的文件名 else if(command.equals("get")){ if(!new File("C:\\",filename).exists()){ //假设文件都是在C盘根目录 System.out.println("没有这个文件,无法提供下载!"); sKey.attach("notexists"); } else sKey.attach("DownloadReady#"+filename); //要保护该通道的文件名 } } else { channel.close(); } } else if(name.startsWith("read_file")){//这儿可以新开一个线程 文件操作也可以用NIO DataOutputStream fileOut = new DataOutputStream( new BufferedOutputStream( new FileOutputStream( new File("C:\\",name.split("#")[1])))); int passlen = channel.read(clientBuffer); while (passlen>=0) { clientBuffer.flip(); fileOut.write(clientBuffer.array(), 0, passlen); passlen = channel.read(clientBuffer); } System.out.println("上传完毕!"); fileOut.close(); channel.close(); } clientBuffer.clear(); } else if (key.isWritable()) { // 写事件 SocketChannel channel = (SocketChannel) key.channel(); String flag = (String) key.attachment(); if(flag.startsWith("downloading")){//这儿可以新开一个线程 文件操作也可以用NIO DataInputStream fis = new DataInputStream( new BufferedInputStream( new FileInputStream( new File("C:\\",flag.split("#")[1])))); byte[] buf = new byte[1024]; int len =0; while ((len = fis.read(buf))!= -1) { channel.write(ByteBuffer.wrap(buf, 0, len)); } fis.close(); System.out.println("文件传输完成"); channel.close(); } else if(flag.equals("notexists")){ //channel.write(encoder.encode(CharBuffer.wrap(flag))); channel.write(ByteBuffer.wrap(flag.getBytes())); //不用编码也行 客户端直接接收 中文也不是乱码 channel.close(); } else if(flag.startsWith("UploadReady")){ channel.write(encoder.encode(CharBuffer.wrap("UploadReady"))); //这儿如果不重新注册该通道的读操作 selector选择到该通道的将继续永远是写操作,也就无法跳转到上面的接受上传的处理 SelectionKey sKey =channel.register(selector, SelectionKey.OP_READ);//register是覆盖的????!!! sKey.attach("read_file#"+flag.split("#")[1]); //key.attach("read_file#"+flag.split("#")[1]); //select不到读操作 } else if(flag.startsWith("DownloadReady")){ channel.write(ByteBuffer.wrap("准备下载".getBytes())); //channel.write(encoder.encode(CharBuffer.wrap("准备下载"))); key.attach("downloading#"+flag.split("#")[1]); } } } public static void main(String[] args) { try { System.out.println("等待来至" + port + "端口的客户端连接....."); new NewSocketServer().setListener(); } catch (Exception e) { e.printStackTrace(); } } }
客户端代码如下:
import java.io.*; import java.net.InetAddress; import java.net.Socket; import java.util.Scanner; public class ClientMain { private int ServerPort = 9527; private String ServerAddress = "192.168.1.154"; private String GetOrPut = "get"; private String local_filename = ""; private String remote_filename = ""; private byte[] buf; private int len; class SocketThread extends Thread{ @Override public void run() { try { File file = new File("C:\\",local_filename); //假设文件放在C盘 if(!file.exists()&&GetOrPut.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+" "+remote_filename);//服务器端如果是io的socket,writeUTF和writeUTF对接 dos.write((GetOrPut+" "+remote_filename).getBytes()); dos.flush(); //String tempString = dis.writeUTF(); buf = new byte[1024]; len = dis.read(buf); String tempString = new String(buf,0,len);//服务器反馈的信息 //System.out.println(tempString); if(tempString.equals("notexists")){ 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("UploadReady")){ 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(); } } catch (Exception e) { e.printStackTrace(); } } } public boolean checkCommand(String command) { if(!command.startsWith("put")&&!command.startsWith("get")){ 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")){ GetOrPut = "put"; local_filename = tempStrings[tempStrings.length-2]; remote_filename = tempStrings[tempStrings.length-1]; } else if(command.startsWith("get")){ GetOrPut = "get"; local_filename = tempStrings[tempStrings.length-1]; remote_filename = tempStrings[tempStrings.length-2]; } return true; } public static void main(String[] args) { ClientMain thisC= new ClientMain(); Scanner sc = new Scanner(System.in); String commandString = ""; do { System.out.println("请输入命令:"); commandString = sc.nextLine(); } while (!thisC.checkCommand(commandString)); ClientMain.SocketThread a = thisC.new SocketThread(); a.start(); } }
相关推荐
java nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socketjava nio 实现socket
基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例 基于NIO的socket举例基于NIO的socket举例 基于NIO的socket举例
非常详细地讲解了NIO中的缓冲区、通道、选择器、编码,以及使用Socket技术实现TCP/IP和UDP编程,细化到了演示全部SocketOption的特性,这对理解基于NIO和Socket技术为基础所开发的NIO框架是非常有好处的,本书以案例...
《NIO与Socket编程技术指南》_高洪岩
nio的socket小玩意儿, 对于初学者有点用处,大家可以相互学习下嘛
使用NIO socket不需要多线程来处理多个连接的请求,效率非常高 可以作为NIO socket入门的例子,Reactor模式,重点理解key.attach, jar文件里包含了源代码 1,运行server.bat启动服务器,可以打开编辑,修改端口号 ...
java NIO 高性能 socket通讯,服务端采用单线程,降低了cpu的压力,普通io socket通讯,server需要每个连接运行个线程,容易出现问题,效率也低
NULL 博文链接:https://wjy320.iteye.com/blog/2002237
本例包含服务器端和客户端,多线程,每线程多次发送,Eclipse工程,启动服务器使用 nu.javafaq.server.NioServer,启动客户端使用 nu.javafaq.client.NioClient。另本例取自javafaq.nv上的程序修改而成
NULL 博文链接:https://b-l-east.iteye.com/blog/1254693
用java编写的nio通信的例子,nio是io编程的新版本,比io较流行。同时本例子是适用socket通信的。可以在此基础上,添加您的个人应用。本例子适用于:java通信的学习者,android平台通信的学习者。
使用NIO channel 实现的加法服务器 附带客户端 服务器端代码 简单易懂
java基于nio的socket通信.rar
基于事件的 NIO 多线程服务器
JDK1.4提供的无阻塞I/O(NIO)有效解决了多线程服务器存在的线程开销问题,但在使用上略显得复杂一些。许多基于NIO的多线程服务器程序往往直接基于选择器(Selector)的Reactor模式实现。这种简单的事件机制对于较...
《NIO与Socket编程技术指南》_高洪岩
java nio 编程一个实例子.服务端程序
NioSocket,包括server端和client端。server端有自动判定client掉线机制,client端有自动重连机制。本人已在项目实用,未经允许禁止转载!
NULL 博文链接:https://j2ee2009.iteye.com/blog/695697
java NIO 创建的服务端,能够异步响应客户端的请求,客户端采用nio异步请求服务端,通信之间的乱码使用charset解决