channel的注冊中心selector是整個NIO的核心,選擇器實作了I/O多路複用。使得單一線程有辦法同時對多個socket通道實作監控并及時發現需要處理的IO事件。選擇的使用比較簡單,主要是三個步驟:
-
1、将通道注冊到一個通道上
這裡建立裡一個Selector,并将
l注冊到selector上ServerSocketChanne
//建立一個selector對象 Selector sel = Selector.open(); // 建立severSocketChannel,并設定為非阻塞的模式 ServerSocketChannel server = sel.provider().openServerSocketChannel(); server.configureBlocking(false); //綁定通道到指定的端口 ServerSocket socket = server.socket(); socket.bind(new InetSocketAddress(8080)); //向selector注冊感興趣的事件 server.register(sel, SelectionKey.OP_ACCEPT);
-
2、選擇器執行select()方法阻塞擷取已經等待就緒的事件
下面是擷取key的示例:
ByteBuffer buffer = ByteBuffer.allocate(1024); try { while(true){ //這是一個阻塞的動作 selector.select(); Set<SelectionKey> keys = selector.selectedKeys(); Iterator<SelectionKey> iterator = keys.iterator(); while(iterator.hasNext()){ SelectionKey key = iterator.next(); //将目前的key 删除掉,防止重複處理 iterator.remove(); processKey(key,selector, buffer); } } }catch (Exception e){ e.printStackTrace(); }
-
3、使用擷取到的key來反向擷取channel,然後進行邏輯處理
下面是處理key的示例:
private void processKey(SelectionKey key, Selector selector, ByteBuffer buffer) throws IOException { if(key.isAcceptable()){ ServerSocketChannel serverSocket = (ServerSocketChannel) key.channel(); SocketChannel accept = serverSocket.accept(); accept.configureBlocking(false); accept.register(selector,SelectionKey.OP_READ,SelectionKey.OP_WRITE); }else if(key.isReadable()){ //讀事件 SocketChannel channel = (SocketChannel) key.channel(); int len = channel.read(buffer); if(len > 0 ){ buffer.flip(); String content = new String(buffer.array()); SelectionKey register = channel.register(selector, SelectionKey.OP_WRITE); register.attach(content); }else{ channel.close(); } }else if(key.isWritable()){ //可寫事件 SocketChannel channel = (SocketChannel) key.channel(); String attach = (String) key.attachment(); ByteBuffer wrap = ByteBuffer.wrap(("輸出内容:" + attach).getBytes()); // channel.write(wrap); } }
這裡我們需要簡單的介紹下
SelectionKey
的四個事件:
- OP_READ:讀事件,目前channel發生讀事件,将會獲得
- OP_WRITE:寫事件。目前channel寫事件就緒的時候觸發
- OP_CONNECT:channel連接配接成功事件。目前channel連接配接事件發生的時候觸發
-
OP_ACCEPT:出現可接入的用戶端事件,主要被服務端使用
下面是這幾個事件對應的預設值
public static final int OP_READ = 1 << 0;
public static final int OP_WRITE = 1 << 2;
public static final int OP_CONNECT = 1 << 3;
*/
public static final int OP_ACCEPT = 1 << 4;