NIO与传统IO对应使用的类:
ServerSocketChannel相当于ServerSocket
SocketChannel 相当于Socket
Selector是NIO的核心类,是负责监听ServerSocketChannel和SocketChannel
(所以NIO是可以实现单线程为多个客户端服务的,而传统IO是不可以的)
SelectionKey 返回的监听结果
原理实例解析:
比如如果餐厅是一个系统的话,serverSocket相当于一个餐厅的大门,只负责大门的看护(监视),也就是监听端口,客人就是socket,所有客人都需要通过大门进入餐厅,餐厅的服务员就相当于一个线程,传统socket是一个如图的模型(一个服务员服务一个客人):
如果NIO也是以一个餐厅系统,它的大门就是ServerSocketChannel ,客户端就是SocketChannel,服务员就是(线程+Selector),其服务员增加了selector能力,即为多个客户服务的能力,同时可以监听大门(欢迎新的客人),如图所示:
Selector.select()============无法看到源码,底层是基于C实现的.
实例代码:
实例代码:
1 package com.NIO; 2 3 import java.io.IOException; 4 import java.net.InetSocketAddress; 5 import java.nio.ByteBuffer; 6 import java.nio.channels.SelectionKey; 7 import java.nio.channels.Selector; 8 import java.nio.channels.ServerSocketChannel; 9 import java.nio.channels.SocketChannel; 10 import java.util.Iterator; 11 12 /** 13 * NIO服务端 14 * 15 * @author -琴兽- 16 */ 17 public class NIOServer { 18 // 通道管理器 19 private Selector selector; 20 21 /** 22 * 获得一个ServerSocket通道,并对该通道做一些初始化的工作 23 * 24 * @param port 25 * 绑定的端口号 26 * @throws IOException 27 */ 28 public void initServer(int port) throws IOException { 29 // 获得一个ServerSocket通道 30 ServerSocketChannel serverChannel = ServerSocketChannel.open(); 31 // 设置通道为非阻塞 32 serverChannel.configureBlocking(false); 33 // 将该通道对应的ServerSocket绑定到port端口 34 serverChannel.socket().bind(new InetSocketAddress(port)); 35 // 获得一个通道管理器 36 this.selector = Selector.open(); 37 // 将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件,注册该事件后, 38 // 当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞。 39 serverChannel.register(selector, SelectionKey.OP_ACCEPT); 40 } 41 42 /** 43 * 采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理 44 * 45 * @throws IOException 46 */ 47 public void listen() throws IOException { 48 System.out.println("服务端启动成功!"); 49 // 轮询访问selector 50 while (true) { 51 // 当注册的事件到达时,方法返回;否则,该方法会一直阻塞 52 selector.select(); 53 // 获得selector中选中的项的迭代器,选中的项为注册的事件 54 Iterator<?> ite = this.selector.selectedKeys().iterator(); 55 while (ite.hasNext()) { 56 SelectionKey key = (SelectionKey) ite.next(); 57 // 删除已选的key,以防重复处理 58 ite.remove(); 59 60 handler(key); 61 } 62 } 63 } 64 65 /** 66 * 处理请求 67 * 68 * @param key 69 * @throws IOException 70 */ 71 public void handler(SelectionKey key) throws IOException { 72 73 // 客户端请求连接事件 74 if (key.isAcceptable()) { 75 handlerAccept(key); 76 // 获得了可读的事件 77 } else if (key.isReadable()) { 78 handelerRead(key); 79 } 80 } 81 82 /** 83 * 处理连接请求 84 * 85 * @param key 86 * @throws IOException 87 */ 88 public void handlerAccept(SelectionKey key) throws IOException { 89 ServerSocketChannel server = (ServerSocketChannel) key.channel(); 90 // 获得和客户端连接的通道 91 SocketChannel channel = server.accept(); 92 // 设置成非阻塞 93 channel.configureBlocking(false); 94 95 // 在这里可以给客户端发送信息哦 96 System.out.println("新的客户端连接"); 97 // 在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限。 98 channel.register(this.selector, SelectionKey.OP_READ); 99 } 100 101 /** 102 * 处理读的事件 103 * 104 * @param key 105 * @throws IOException 106 */ 107 public void handelerRead(SelectionKey key) throws IOException { 108 // 服务器可读取消息:得到事件发生的Socket通道 109 SocketChannel channel = (SocketChannel) key.channel(); 110 // 创建读取的缓冲区 111 ByteBuffer buffer = ByteBuffer.allocate(1024); 112 int read = channel.read(buffer); 113 if(read > 0){ 114 byte[] data = buffer.array(); 115 String msg = new String(data).trim(); 116 System.out.println("服务端收到信息:" + msg); 117 118 //回写数据 119 ByteBuffer outBuffer = ByteBuffer.wrap("好的".getBytes()); 120 channel.write(outBuffer);// 将消息回送给客户端 121 }else{ 122 System.out.println("客户端关闭"); 123 key.cancel(); 124 } 125 } 126 127 /** 128 * 启动服务端测试 129 * 130 * @throws IOException 131 */ 132 public static void main(String[] args) throws IOException { 133 NIOServer server = new NIOServer(); 134 server.initServer(8000); 135 server.listen(); 136 } 137 138 }