Java NIO Selector詳解
Selector selector = Selector.open();
將 Channel 注冊(cè)到選擇器中
channel.configureBlocking(false);SelectionKey?key?=?channel.register(selector,?SelectionKey.OP_READ);
注意,如果一個(gè) Channel 要注冊(cè)到 Selector 中,那么這個(gè) Channel 必須是非阻塞的,即channel.configureBlocking(false);
因?yàn)?Channel 必須要是非阻塞的,因此 FileChannel 是不能夠使用選擇器的,因?yàn)?FileChannel 都是阻塞的.
Connect,即連接事件(TCP 連接),?對(duì)應(yīng)于SelectionKey.OP_CONNECT。
Accept,即確認(rèn)事件,對(duì)應(yīng)于SelectionKey.OP_ACCEPT。
Read,即讀事件,對(duì)應(yīng)于SelectionKey.OP_READ, 表示 buffer 可讀。
Write,即寫(xiě)事件,對(duì)應(yīng)于SelectionKey.OP_WRITE, 表示 buffer 可寫(xiě)。
一個(gè) Channel發(fā)出一個(gè)事件也可以稱(chēng)為對(duì)于某個(gè)事件, Channel 準(zhǔn)備好了。因此一個(gè) Channel 成功連接到了另一個(gè)服務(wù)器也可以被稱(chēng)為 connect ready。
int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;
channel.register(selector, SelectionKey.OP_READ);channel.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
int interestSet = selectionKey.interestOps();boolean isInterestedInAccept = interestSet & SelectionKey.OP_ACCEPT;boolean isInterestedInConnect = interestSet & SelectionKey.OP_CONNECT;boolean isInterestedInRead = interestSet & SelectionKey.OP_READ;boolean isInterestedInWrite = interestSet & SelectionKey.OP_WRITE;
int?readySet?=?selectionKey.readyOps();selectionKey.isAcceptable();selectionKey.isConnectable();selectionKey.isReadable();selectionKey.isWritable();
Channel 和 Selector
Channel channel = selectionKey.channel();Selector selector = selectionKey.selector();
Attaching Object
selectionKey.attach(theObject);Object attachedObj = selectionKey.attachment();
SelectionKey key = channel.register(selector, SelectionKey.OP_READ, theObject);
通過(guò) Selector 選擇 Channel
注意:select()方法返回的值表示有多少個(gè) Channel 可操作.
獲取可操作的 Channel
SetselectedKeys = selector.selectedKeys(); IteratorkeyIterator = selectedKeys.iterator(); while(keyIterator.hasNext()) {SelectionKey key = keyIterator.next();if(key.isAcceptable()) {// a connection was accepted by a ServerSocketChannel.} else if (key.isConnectable()) {// a connection was established with a remote server.} else if (key.isReadable()) {// a channel is ready for reading} else if (key.isWritable()) {// a channel is ready for writing}keyIterator.remove();}
-
通過(guò) Selector.open() 打開(kāi)一個(gè) Selector. -
將 Channel 注冊(cè)到 Selector 中, 并設(shè)置需要監(jiān)聽(tīng)的事件(interest set) -
不斷重復(fù): *從 selected key 中獲取 對(duì)應(yīng)的 Channel 和附加信息(如果有的話(huà))
關(guān)閉 Selector
完整的 Selector 例子
public class NioEchoServer {private static final int BUF_SIZE = 256;private static final int TIMEOUT = 3000;public static void main(String args[]) throws Exception {// 打開(kāi)服務(wù)端 SocketServerSocketChannel serverSocketChannel = ServerSocketChannel.open();// 打開(kāi) SelectorSelector selector = Selector.open();// 服務(wù)端 Socket 監(jiān)聽(tīng)8080端口, 并配置為非阻塞模式serverSocketChannel.socket().bind(new InetSocketAddress(8080));serverSocketChannel.configureBlocking(false);// 將 channel 注冊(cè)到 selector 中.// 通常我們都是先注冊(cè)一個(gè) OP_ACCEPT 事件, 然后在 OP_ACCEPT 到來(lái)時(shí), 再將這個(gè) Channel 的 OP_READ// 注冊(cè)到 Selector 中.serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);while (true) {// 通過(guò)調(diào)用 select 方法, 阻塞地等待 channel I/O 可操作if (selector.select(TIMEOUT) == 0) {System.out.print(".");continue;}// 獲取 I/O 操作就緒的 SelectionKey, 通過(guò) SelectionKey 可以知道哪些 Channel 的哪類(lèi) I/O 操作已經(jīng)就緒.IteratorkeyIterator = selector.selectedKeys().iterator(); while (keyIterator.hasNext()) {SelectionKey key = keyIterator.next();// 當(dāng)獲取一個(gè) SelectionKey 后, 就要將它刪除, 表示我們已經(jīng)對(duì)這個(gè) IO 事件進(jìn)行了處理.keyIterator.remove();if (key.isAcceptable()) {// 當(dāng) OP_ACCEPT 事件到來(lái)時(shí), 我們就有從 ServerSocketChannel 中獲取一個(gè) SocketChannel,// 代表客戶(hù)端的連接// 注意, 在 OP_ACCEPT 事件中, 從 key.channel() 返回的 Channel 是 ServerSocketChannel.// 而在 OP_WRITE 和 OP_READ 中, 從 key.channel() 返回的是 SocketChannel.SocketChannel clientChannel = ((ServerSocketChannel) key.channel()).accept();clientChannel.configureBlocking(false);//在 OP_ACCEPT 到來(lái)時(shí), 再將這個(gè) Channel 的 OP_READ 注冊(cè)到 Selector 中.// 注意, 這里我們?nèi)绻麤](méi)有設(shè)置 OP_READ 的話(huà), 即 interest set 仍然是 OP_CONNECT 的話(huà), 那么 select 方法會(huì)一直直接返回.clientChannel.register(key.selector(), OP_READ, ByteBuffer.allocate(BUF_SIZE));}if (key.isReadable()) {SocketChannel clientChannel = (SocketChannel) key.channel();ByteBuffer buf = (ByteBuffer) key.attachment();long bytesRead = clientChannel.read(buf);if (bytesRead == -1) {clientChannel.close();} else if (bytesRead > 0) {key.interestOps(OP_READ | SelectionKey.OP_WRITE);System.out.println("Get data length: " + bytesRead);}}if (key.isValid() && key.isWritable()) {ByteBuffer buf = (ByteBuffer) key.attachment();buf.flip();SocketChannel clientChannel = (SocketChannel) key.channel();clientChannel.write(buf);if (!buf.hasRemaining()) {key.interestOps(OP_READ);}buf.compact();}}}}}
轉(zhuǎn)載源:SegmentFault
特別推薦一個(gè)分享架構(gòu)+算法的優(yōu)質(zhì)內(nèi)容,還沒(méi)關(guān)注的小伙伴,可以長(zhǎng)按關(guān)注一下:
長(zhǎng)按訂閱更多精彩▼
如有收獲,點(diǎn)個(gè)在看,誠(chéng)摯感謝
免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀(guān)點(diǎn),不代表本平臺(tái)立場(chǎng),如有問(wèn)題,請(qǐng)聯(lián)系我們,謝謝!





