package com.study.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class NIOServer { private static int port = 9995; //端口 // 1.第一个是服务 private static ServerSocketChannel server; // 2.第二个是多路复用器 private static Selector selector; // 3.第三个就是缓冲区buffer // 信息接收 ByteBuffer rcBuffer = ByteBuffer.allocate(1024); // 信息写出 ByteBuffer sendBuffer = ByteBuffer.allocate(1024); // 4.维护一个事件标签集合,和selector配合使用 Map<SelectionKey,String> sessionMsg = new HashMap<SelectionKey,String>(); // 初始化 public NIOServer() throws IOException{ server = ServerSocketChannel.open(); // 打开服务端,服务端临听端口 server.socket().bind(new InetSocketAddress(port)); // server.configureBlocking(false); //设置为非阻塞,默认是true selector = Selector.open(); //实例化多路复用器 //server注册上多路复用器之后,多路复用器才会为server工作,实现一个server为成千上万个client工作 //selector和事件标签Selectionkey是一起应用的,第一次服务器,是接受,Accept server.register(selector, SelectionKey.OP_ACCEPT); System.out.println("NIO服务器已启动,且监听的端口是" + port); //this.port范围加大 } // 监听请求方法 public void listen() throws IOException{ while(true){ // 进来就是通过多路复用器selector,来看是否有注册事件 int eventCount = selector.select(); // 已注册事件数量 if(eventCount == 0){ // selector继续轮询,NIO的内部机制就是不断轮询注册到selector上面的多个channel continue; } // 拿到事件集合,SelectorKey的作用就是获取这些就绪通道的集合 Set<SelectionKey> keys = selector.selectedKeys(); //迭代 Iterator<SelectionKey> iterator = keys.iterator(); while(iterator.hasNext()){ SelectionKey key = iterator.next(); //这个事件就是客户端的一个读或者写 process(key); //处理事件 //处理完后,事件移出集合 iterator.remove(); } } } // 处理客户端事件 private void process(SelectionKey key) { // 处理客户端请求,定义客户端对象 SocketChannel client = null; try { // 判断key是否是有效的 if(key.isValid() && key.isAcceptable()){ client = server.accept(); //接收一个客户端 client.configureBlocking(false); //设置为非阻塞 client.register(selector , SelectionKey.OP_READ); //读取客户端请求,事件标签类型改为读取 } else if(key.isValid() && key.isReadable()){ //是否是可读的 // 读到缓冲区 rcBuffer.clear(); client = (SocketChannel)key.channel(); // 拿到客户端通道 int len = client.read(rcBuffer); //读取的长度 if(len > 0){ //读到内容 String msg = new String(rcBuffer.array() , 0 ,len); System.out.println("服务端收到msg:"+msg); sessionMsg.put(key, msg); // 告诉selector,已读完,下次可以写数据 client.register(selector, SelectionKey.OP_WRITE); } }else if(key.isValid() && key.isWritable()){ if(!sessionMsg.containsKey(key)){ //是否有消息需要回 return; } //回复消息 client = (SocketChannel)key.channel(); // 拿到客户端通道 sendBuffer.clear(); sendBuffer.put(new String(sessionMsg.get(key)+ "你好,你的请求已完成").getBytes()); //设置读取位 sendBuffer.flip(); // 缓冲区的内容写出去 client.write(sendBuffer); // 再次注册入selector client.register(selector, SelectionKey.OP_READ); } } catch (IOException e) { try { //读取key事件时,遇到客户端异常下线,不会引起异常 key.cancel(); client.socket().close(); client.close(); } catch (IOException e1) { e1.printStackTrace(); } } } public static void main(String[] args) { try { NIOServer server = new NIOServer(); server.listen(); } catch (IOException e) { e.printStackTrace(); } } }
CLIENT
package com.study.nio; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.ByteBuffer; import java.nio.channels.SelectionKey; import java.nio.channels.Selector; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import java.util.Set; public class NIOClient { private static int port = 9995; //端口 // 1.第一个是服务 private static ServerSocketChannel clientC; // 2.第二个是多路复用器 private static Selector selector; // 3.第三个就是缓冲区buffer // 信息接收 ByteBuffer rcBuffer = ByteBuffer.allocate(1024); // 信息写出 ByteBuffer sendBuffer = ByteBuffer.allocate(1024); // 4.维护一个事件标签集合,和selector配合使用 Map<SelectionKey,String> sessionMsg = new HashMap<SelectionKey,String>(); // 初始化 public NIOClient() throws IOException{ clientC = ServerSocketChannel.open(); // 打开服务端,服务端临听端口 clientC.socket().bind(new InetSocketAddress(port)); // clientC.configureBlocking(false); //设置为非阻塞,默认是true selector = Selector.open(); //实例化多路复用器 //server注册上多路复用器之后,多路复用器才会为server工作,实现一个server为成千上万个client工作 //selector和事件标签Selectionkey是一起应用的,第一次服务器,是接受,Accept clientC.register(selector, SelectionKey.OP_ACCEPT); System.out.println("NIO服务器已启动,且监听的端口是" + port); //this.port范围加大 } // 监听请求方法 public void listen() throws IOException{ while(true){ // 进来就是通过多路复用器selector,来看是否有注册事件 int eventCount = selector.select(); // 已注册事件数量 if(eventCount == 0){ // selector继续轮询,NIO的内部机制就是不断轮询注册到selector上面的多个channel continue; } // 拿到事件集合,SelectorKey的作用就是获取这些就绪通道的集合 Set<SelectionKey> keys = selector.selectedKeys(); //迭代 Iterator<SelectionKey> iterator = keys.iterator(); while(iterator.hasNext()){ SelectionKey key = iterator.next(); //这个事件就是客户端的一个读或者写 process(key); //处理事件 //处理完后,事件移出集合 iterator.remove(); } } } // 处理客户端事件 private void process(SelectionKey key) { // 处理客户端请求,定义客户端对象 SocketChannel client = null; try { // 判断key是否是有效的 if(key.isConnectable()){ client = (SocketChannel)key.channel(); // 拿到客户端通道 client.configureBlocking(false); //设置为非阻塞 client.register(selector , SelectionKey.OP_READ); //读取客户端请求,事件标签类型改为读取 } else if(key.isValid() && key.isReadable()){ //是否是可读的 // 读到缓冲区 rcBuffer.clear(); client = (SocketChannel)key.channel(); // 拿到客户端通道 int len = client.read(rcBuffer); //读取的长度 if(len > 0){ //读到内容 String msg = new String(rcBuffer.array() , 0 ,len); sessionMsg.put(key, msg); // 告诉selector,已读完,下次可以写数据 client.register(selector, SelectionKey.OP_WRITE); } }else if(key.isValid() && key.isWritable()){ if(!sessionMsg.containsKey(key)){ //是否有消息需要回 return; } //回复消息 client = (SocketChannel)key.channel(); // 拿到客户端通道 sendBuffer.clear(); sendBuffer.put(new String(sessionMsg.get(key)+ "你好,你的请求已完成").getBytes()); //设置读取位 sendBuffer.flip(); // 缓冲区的内容写出去 client.write(sendBuffer); // 再次注册入selector client.register(selector, SelectionKey.OP_READ); } } catch (IOException e) { try { //读取key事件时,遇到客户端异常下线,不会引起异常 key.cancel(); client.socket().close(); client.close(); } catch (IOException e1) { e1.printStackTrace(); } } } public static void main(String[] args) { try { NIOServer server = new NIOServer(); server.listen(); } catch (IOException e) { e.printStackTrace(); } } }