比喻:餐厅

单线程NIO和单线程BIO一样,它只有一个员工,这个员工有分流(Select)的能力
- 他负责接待新来的客人,安排客人坐下,给客人菜单(安顿客人 事件)
- 客人要点单了,他过去帮客人下单(帮客人下单 事件)
BIO处理的单元是用户;而NIO处理的单元是事件
- BIO,有客人来,我去跟踪这个客人为它服务(普通函数的思想)
- NIO,安顿客人事件发生,我触发这个事件,并处理这个事件(Reactor模式)
代码
服务端代码
package NIO;//NIO的服务器(餐厅)public class NIOServer{//通道管理器(服务员)private Selector selector;//(餐厅开门)//获得一个ServerSocket通道,并对该通道做一些初始化的工作public void initServer(int port) throws IOException{//获得一个ServerSocket通道(开一扇门)ServerSocketChannel serverChannel = ServerSocketChannel.open();serverChannel.configureBlocking(false); //设置通道为非阻塞serverChannel.socket().bind(new InetSocketAddress(port)); //将该通道对应的ServerSocket绑定到port端口//获得一个通道管理器(服务员)this.selector = Selector.open();//(将此服务员安排在这个餐厅;并将餐厅的门打开)//将通道管理器和该通道绑定,并为该通道注册SelectionKey.OP_ACCEPT事件//当该事件到达时,selector.select()会返回,如果该事件没到达selector.select()会一直阻塞serverChannel.register(selector, SelectionKey.OP_ACCEPT);}//(服务员在这里领任务:看是否有客人来;是否有人点餐;……)//采用轮询的方式监听selector上是否有需要处理的事件,如果有,则进行处理public void listen() throws IOException{System.out.println("服务器启动成功!");//轮询访问selectorwhile(true) {//(服务员看看有没有事情要处理// 1. 如果没事情,就会堵塞在这一句,等待事情来;// 2. 如果有事情,就继续下面的代码// )//当注册的事件到达时,方法返回;否则,该方法会一直阻塞selector.select();//(取出所有的事情,一件一件做)//获得selector中选中的项的迭代器,选中的项为注册的事件Iterator<?> ite = this.selector.selectedKeys().iterator();while(ite.hasNext()){//(服务员要处理的事情名称)SelectionKey key = (SelectionKey)ite.next();//(删除这个事情,表示已经做了)//删除已选的key,以防重复处理ite.remove();//(去处理这个事情)//处理这个keyhandler(key);}}}//(服务员干活)//处理请求public void handler(SelectionKey key) throws IOException{if(key.isAcceptable()) {//(有客人来)//客户端请求连接事件handlerAccept(key);} else if(key.isReadable()){//(客人要点餐)//获得可读的事件handelerRead(key);}}//(服务员如何安排客人坐下)//处理连接请求public void handlerAccept(SelectionKey key) throws IOException {ServerSocketChannel server = (ServerSocketChannel) key.channel();SocketChannel channel = server.accept(); //获得和客户端连接的通道channel.configureBlocking(false); //设置成非阻塞System.out.println("新的客户端连接");//在这里可以给客户端发送信息//...//(客户坐下以后,把菜单给他,给他点菜的权限)//在和客户端连接成功之后,为了可以接收到客户端的信息,需要给通道设置读的权限channel.register(this.selector, SelectionKey.OP_READ);}//(服务员如何取得客人的菜单)//处理读的事件public void handelerRead(SelectionKey key) throws IOException {//服务器可读取消息:得到事件发生的Socket通道SocketChannel channel = (SocketChannel) key.channel();//创建读取缓冲区ByteBuffer buffer = ByteBuffer.allocate(1024);channel.read(buffer);byte[] data = buffer.array();String msg = new String(data).trim();System.out.println("服务端收到的信息:" + msg);ByteBuffer outBuffer = ByteBuffer.wrap(msg.getBytes());channel.write(outBuffer); //将消息送回给客户端}//(正式开门)//启动服务端测试public static void main(String[] args) throws IOException{NIOServer server = new NIOServer();server.initServer(8000);server.listen();}}
客户端:可以使用windows的telnet
# 1. 打开Windows命令行# 2. 使用telnet连接服务端> telnet 127.0.0.1 8000# 3. 给服务端发送消息> send wo yao chifan
