Selector
Selector(選擇器)是Java NIO中能夠檢測一到多個(gè)NIO通道,并能夠知曉通道是否為諸如讀寫事件做好準(zhǔn)備的組件。這樣能耻,一個(gè)單獨(dú)的線程可以管理多個(gè)channel亡驰,從而管理多個(gè)網(wǎng)絡(luò)連接 凡辱,減少服務(wù)器的性能開銷。
創(chuàng)建Selector
-
通過Selector 提供的靜態(tài)方法創(chuàng)建
Selector selector = Selector.open();
-
通過SelectorProvider獲取選擇器
SelectorProvider selectorProvider = SelectorProvider.provider(); Selector selector = selectorProvider.openSelector();
將通道注冊到選擇器上
通過ServerSocketChannel.register(Selector sel, int ops)方法注冊的selecor中
//獲取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//切換到非堵塞模式
serverSocketChannel.configureBlocking(false);
//綁定端口號
serverSocketChannel.bind(new InetSocketAddress(8080));
//獲取選擇器
Selector selector = Selector.open();
//將通道注冊到選擇器上,并且指定“監(jiān)聽接收事件”
SelectionKey key = serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
與selector使用乳乌,channel必須處于非堵塞狀態(tài)下汉操,F(xiàn)ileChannel無法切換到非堵塞狀態(tài)下將不能與selector一起使用。
register()中的第二個(gè)參數(shù)表示通道監(jiān)聽的事件一共四種狀態(tài)
- SelectionKey.OP_CONNECT 連接
- SelectionKey.OP_ACCEPT 接收
- SelectionKey.OP_READ 讀
- SelectionKey.OP_WRITE 寫
單一個(gè)通道監(jiān)聽多種事件時(shí)使用位或
多種事件如: SelectionKey.OP_CONNECT|SelectionKey.OP_ACCEPT
Selector的select()方法
Selector中注冊了一或多個(gè)通道磷瘤,就可以調(diào)用幾個(gè)重載的select()方法芒篷。這些方法返回通道的事件(如連接、接受采缚、讀或?qū)懀┮呀?jīng)準(zhǔn)備就緒的那些通道针炉。換句話說,如果你對“讀就緒”的通道感興趣仰担,select()方法會返回讀事件已經(jīng)就緒的那些通道糊识。
下面是select()方法:
- int select()
- int select(long timeout)
- int selectNow()
select()
阻塞到至少有一個(gè)通道在你注冊的事件上就緒了。
select(long timeout)
和select()一樣摔蓝,除了最長會阻塞timeout毫秒(參數(shù))赂苗。
selectNow()
不會阻塞,不管什么通道就緒都立刻返回(此方法執(zhí)行非阻塞的選擇操作贮尉。如果自從前一次選擇操作后,沒有通道變成可選擇的猜谚,則此方法直接返回零败砂。)赌渣。
select()方法返回的int值表示有多少通道已經(jīng)就緒。亦即昌犹,自上次調(diào)用select()方法后有多少通道變成就緒狀態(tài)坚芜。如果調(diào)用select()方法,因?yàn)橛幸粋€(gè)通道變成就緒狀態(tài)斜姥,返回了1鸿竖,若再次調(diào)用select()方法,如果另一個(gè)通道就緒了铸敏,它會再次返回1缚忧。如果對第一個(gè)就緒的channel沒有做任何操作,現(xiàn)在就有兩個(gè)就緒的通道杈笔,但在每次select()方法調(diào)用之間闪水,只有一個(gè)通道就緒了。
selectedKeys()
一旦調(diào)用了select()方法蒙具,并且返回值表明有一個(gè)或更多個(gè)通道就緒了球榆,然后可以通過調(diào)用selector的selectedKeys()方法,訪問“選擇降炅俊(已就緒的監(jiān)聽事件)”中的就緒通道芜果。如下所示:
Set selectedKeys = selector.selectedKeys()
使用Selector實(shí)現(xiàn)非堵塞Socket
服務(wù)端
//1.獲取通道
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
//2.切換到非堵塞模式
serverSocketChannel.configureBlocking(false);
//3.綁定端口號
serverSocketChannel.bind(new InetSocketAddress(8080));
//4.獲取選擇器
Selector selector = Selector.open();
//5.將通道注冊到選擇器上,并且指定“監(jiān)聽接收事件”
serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT);
//6輪詢式的獲取選擇器上已經(jīng)‘準(zhǔn)備就緒’的事件
while (selector.select()>0){
//7 融师。獲取當(dāng)前選擇器中所有注冊的"選擇接壹亍(已就緒的監(jiān)聽事件)"
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()){
//8.獲取“準(zhǔn)備就緒”的事件
SelectionKey selectionKey = iterator.next();
//9.判斷具體事件,就緒
if (selectionKey.isAcceptable()){
//10.接收就緒旱爆,獲取客戶端連接
SocketChannel socketChannel = serverSocketChannel.accept();
//11,切換到非堵塞模式
socketChannel.configureBlocking(false);
//12.將客戶端通道注冊到選擇器上
socketChannel.register(selector,SelectionKey.OP_READ);
}else if (selectionKey.isReadable()){
//獲取當(dāng)前選擇器上“讀就緒”狀態(tài)的通道
SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
ByteBuffer buffer = ByteBuffer.allocate(1024);
//讀取客戶端傳過來的數(shù)據(jù)
int len = 0;
while ((len = socketChannel.read(buffer))>0){
buffer.flip();
System.out.println(new String(buffer.array(),0,len));
buffer.clear();
}
}
//取消選擇鍵selectionKey
iterator.remove();
}
}
客戶端
//1.獲取通道
SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
//2.設(shè)置為非堵塞模式
socketChannel.configureBlocking(false);
ByteBuffer buf = ByteBuffer.allocate(1024);
//3.發(fā)送數(shù)據(jù)給服務(wù)端
//控制臺輸入數(shù)據(jù)
Scanner scanner = new Scanner(System.in);
while (scanner.hasNext()){
String msg = scanner.next();
buf.put(msg.getBytes());
buf.flip();
socketChannel.write(buf);
buf.clear();
}
//4.關(guān)閉連接
socketChannel.close();