java NIO Selector 概念和例子

java NIO Selector 可以監控多個Channel 實例,確定哪個Channel需要準備讀或者寫。單個線程管理多個Channel。

為什麼使用Selector,為什麼Selector要用單線程?

因為切換線程和創建都需要消耗系統的資源,下面塗是Selector管理Channel的圖:

java NIO Selector 概念和例子

下面我們看一個例子:


public class NIOClient {
SocketChannel channel;
public void initClient(String host,int port) throws IOException{
InetSocketAddress servAdds = new InetSocketAddress(host,port); //地址
this.channel = SocketChannel.open(servAdds); //SocketChannel.open() 通過InetSocketAddress打開一個通道
}
public void sendAndRecv(String words) throws IOException{
byte[] msg = new String(words).getBytes();
ByteBuffer buffer = ByteBuffer.wrap(msg);
System.out.println("client 發送數據:"+words);
channel.write(buffer); //通過buffer寫數據到channel
buffer.clear();
channel.read(buffer); //讀數據
System.out.println("client 接收到數據:"+new String(buffer.array()).trim());
channel.close(); //channel用完關閉
}
public static void main(String[] args) throws IOException{
NIOClient client = new NIOClient();
client.initClient("localhost",9999);
client.sendAndRecv("showm me the money");
}
}
package xin.clips.nio.selector;
import java.io.IOException;
import java.net.InetSocketAddress;
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;
public class NIOServer {
private Selector selector;
private ServerSocketChannel serverSocketChannel;
public void initServer(int port) throws IOException {
serverSocketChannel = ServerSocketChannel.open(); //通過靜態open()方法打開ServerSocketChannel
serverSocketChannel.configureBlocking(false); //設置為非阻塞模式
serverSocketChannel.socket().bind(new InetSocketAddress("localhost", port)); //服務器監聽

this.selector = Selector.open();
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
}
public void listen() throws IOException {
System.out.println("服務器啟動成功! ");
while (true) { //select 會出現空循環,浪費CPU資源
selector.select(); // 進行阻塞blocking
Iterator<selectionkey> its = selector.selectedKeys().iterator();
while (its.hasNext()) {
SelectionKey key = its.next();
if (key.isAcceptable()) {
SocketChannel channel = serverSocketChannel.accept(); //打開通道
channel.configureBlocking(false);//設置非阻塞模式
channel.register(selector, SelectionKey.OP_READ);
} else if (key.isReadable()) {
//準備讀取
recvAndReply(key);
}
its.remove(); //移除
}
}
}
public void recvAndReply(SelectionKey key) throws IOException {
SocketChannel channel = (SocketChannel) key.channel();
ByteBuffer buffer = ByteBuffer.allocate(256);
int i = channel.read(buffer);
if (i != -1) {
//能獲取到數據
String msg = new String(buffer.array()).trim();
System.out.println("nio 服務器收到信息:" + msg);
System.out.println("nio 服務器返回:" + msg);
channel.write(ByteBuffer.wrap(msg.getBytes())); //返回數據
} else {
//沒有數據
channel.close();
}
}
public static void main(String[] args) throws IOException {
NIOServer server = new NIOServer();
server.initServer(9999);
server.listen();
}
}
/<selectionkey>

NIO server 程序輸出結果:

nio 服務器收到信息:showm me the money
nio 服務器返回:showm me the money
nio 服務器收到信息:showm me the money
nio 服務器返回:showm me the money

NIO Client 程序輸出結果:

client 發送數據:showm me the money
client 接收到數據:showm me the money
Disconnected from the target VM, address: '127.0.0.1:64825', transport: 'socket'
Process finished with exit code 0

程序分析:

1、Selector創建需要通過Selector.open() 方法,通過Selector工廠類開啟

2、在Channel中註冊Selector,通過channel.register(select,SelectionKey.OP_READ);方法

3、Channel需要使用Selector必須使用非阻塞的Channel,FileChannel是阻塞模式的,所以它不能配合使用Selector

4、register()方法表示"興趣集",表示Selector需要監聽的Channel的10:25:35,比如Connect,Accept,Read,Write,這4種事件對應SelectionKey中的常量:

SelectionKey.OP_CONNECT

SelectionKey.OP_ACCEPT

SelectionKey.OP_READ

SelectionKey.OP_WRITE

5、select.select()方法調用後,服務器端程序進入阻塞狀態,等到監聽事件觸發後,select()方法結果,通過迭代select.selectedKeys()進行匹配處理定義的情況。

代碼地址:https://github.com/yuanzongyu/tutorials/tree/master/core-java-io/src/main/java/xin/clips/nio


分享到:


相關文章: