/** * User: canhun@taobao.com * Date: 14-4-23 * Time: PM1:55 */ import java.io.IOException; import java.net.InetSocketAddress; import java.net.ServerSocket; 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.Iterator; import java.util.Set; /** * User: kyle * Date: 14-1-3 * Time: PM1:11 * 该demo主要用于我学习java 原生提供的nio api.通过一个基本的小demo来理解nio api的基本功能,从而加深netty对nio api的抽象和封装 * netty的IO模型: * eventloop: * 是对selector,selectionKeys,thread的封装.每一个eventloop封装着一个selector以及这个selector收集到的selectionKeys, * 然后提供一个接口方法register方法把selector注册到channel(这个channel可能是一个ServerSocketChannel也可能是一个SocketChannel中。 * 最后把提供了一个thread和一个taskQueue,把IO以及其他任务放到这个taskQueue,让thread执行 * * eventloop_group: * 是对一组eventloop的封装,内部维护着一个名字为child[]的EventExecutor(eventloop的父类)类型的数组 * * channel: 有一个parent eventloop group, * * Bootstrap: * */ public class PlainNioEchoServer { public void server(int port) throws IOException { System.out.println("Listening for connections on port :" + port); //1.创建服务端通道对象 ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); //2.创建服务端网络通信对象 ServerSocket ss = serverSocketChannel.socket(); //3.创建并绑定端口 InetSocketAddress address = new InetSocketAddress(port); ss.bind(address); serverSocketChannel.configureBlocking(false); //4.构建选择器 Selector selector = Selector.open(); /** *5.在Selector上注册服务端通道对象,对于serverSocketChannel来说,因为它主要是监听端口, * 等待client来连接,所以只会产生OP_ACCEPT事件 * **/ SelectionKey selectionKey = serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT); while (true) { try { /** * 6.Selector阻塞等待监听serverSocketChannel在第5步上注册的事件,在没有执行下面代码之前, * 现在这个selector只监控OP_ACCPET事件 */ selector.select(); } catch (IOException ex) { ex.printStackTrace(); break; } //7.当有事件发生时,从selector中找到selectedKeys Set readyKeys = selector.selectedKeys(); Iterator iterator = readyKeys.iterator(); //8.处理selector收集到的事件selectedKey集合 while (iterator.hasNext()) { SelectionKey key = (SelectionKey) iterator.next(); iterator.remove(); try { //是否是监听到的是OP_ACCEPT事件 if (key.isAcceptable()) { /** * 8.1 * 得到当前发生事件的Channel对象,因为只有ServerSocketChannel在selector中注册了OP_ACCEPT事件 * 所以这里key对应的channel应该是ServerSocketChannel类型的 */ ServerSocketChannel server = (ServerSocketChannel) key.channel(); //8.2 接受客户端的连接请求,并产生对应的客户端SocketChannel对象 SocketChannel client = server.accept(); System.out.println("Accepted connection from " + client); client.configureBlocking(false); /** * 8.3 * 在Selector上注册client SocketChannel感兴趣的读和写事件 * 因为SocketChannel会向client read和write消息,所以这里会向selector注册OP_WRITE和OP_READ事件 * PS:Server监听OP_ACCEPT事件的selector和Server accpet后产生的SocketChannel监听OP_WRITE、OP_READ * 事件的selector可以是相同的selector(本demo),也可以是不同的selector(netty) */ client.register(selector, SelectionKey.OP_WRITE | SelectionKey.OP_READ, ByteBuffer.allocate(100)); } //是否是监听到的OP_READ事件 if (key.isReadable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer output = (ByteBuffer) key.attachment(); client.read(output); } //是否是监听到的OP_WRITE事件 if (key.isWritable()) { SocketChannel client = (SocketChannel) key.channel(); ByteBuffer output = (ByteBuffer) key.attachment(); output.flip(); client.write(output); output.compact(); } } catch (IOException ex) { key.cancel(); try { key.channel().close(); } catch (IOException cex) { } } } } } public static void main(String[] args) throws IOException { new PlainNioEchoServer().server(9000); } }