NIO編程一直不太熟悉,一方面沒有系統地學習過,另外一方面NIO編程確實有些複雜,一下沒有用了過陣子容易忘記,因此,寫篇小博客就頗有必要,這裏來總結一下selector組件用法,Buffer和Channel有空補上。java
一張你可能熟悉的圖(IO多路複用)編程
selector(選擇器),IO多路複用的組件, 和它直接關聯的組件是Channel, 它的做用就是不斷的輪詢綁定在他身上的channel。一旦有通道發生了它感興趣的事件,接下來處理該事件。
socket
selector維護了三個set集合,裏面封裝的是 SelectionKey,用它來獲取channel。學習
2.1 key set
spa
全集,每當channel經過register方法註冊進選擇器時,會把包含Channel本身信息的key添加到這個全集中來,註冊的信息就會以SelectionKey的封裝形式保存在這個集合中, 用這個SelectionKey來獲取channel。操作系統
2.2 selected key setcode
感興趣的key的集合,每次遍歷selected key時咱們會執行這行代碼:Set<SelectionKey> selectionKeys = selector.selectedKeys();
cdn
2.3 cannelled key setserver
通常會在客戶端主動斷開鏈接的時候使用它,表明原來感興趣的事件,如今不感興趣了,下一次輪詢,進行select()
本集合中的SelectionKey會從key set中移除, 意味着它所關聯的channel將會被選擇器丟棄掉,再也不進行監聽。
對象
1. 服務端建立表明服務端的Channel,綁定好端口,設置成非阻塞的通道 而且初始化選擇器,而後開始輪詢綁定在本身身上的通道,此時的通道只有一個ServerSocketChannel
,而選擇器只關心ServerSocketChannel
上發生的OP_ACCEPT
事件,而又沒有客戶端來連接 因此他被阻塞在了select()
// 服務端
// 獲取服務端的SerSokcetChannel
ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
// 配置成非阻塞的
serverSocketChannel.configureBlocking(false);
// 從通道中獲取服務端的對象
ServerSocket serverSocket = serverSocketChannel.socket();
serverSocket.bind(new InetSocketAddress(1234));
// 建立選擇器
Selector selector = Selector.open();
// 把通到註冊到選擇器上
serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
while (true) {
// 阻塞式等待 channel上有事件發生
int select = selector.select();
}
複製代碼
select 方法概要
select(long); // 設置超時時間,在這個時間範圍內輪詢關心的事件(如上面的OP_ACCEPT事件)
selectNow(); // 當即返回,不阻塞
select(); // 阻塞輪詢複製代碼
2. 客戶端建立表明本身的SocketChannel
, 建立選擇器,把本身感興趣的事件註冊在上面,以下代碼, 初始化本身,SocketChannel
, 把客戶端的通道註冊進選擇器,並告訴選擇器SocketChannel
的感興趣事件是OP_CONNECT
鏈接事件
// 獲取客戶端的通道
SocketChannel socketChannel = SocketChannel.open();
socketChannel.configureBlocking(false);
Selector selector = Selector.open();
// 把客戶端的通道註冊進選擇器
socketChannel.register(selector, SelectionKey.OP_CONNECT);
// 鏈接客戶端, 執行完這行代碼後, 服務端就能就收到通知
socketChannel.connect(new InetSocketAddress("localhost", 1234));
while (true) {
int number = selector.select(); // 選擇器阻塞式的等待 Channel上發生它關心的事件
System.out.println(" 發生了感興趣的事件: " + number);
Set<SelectionKey> keySet = selector.selectedKeys();
// 驗證
for (SelectionKey selectionKey : keySet) {
SocketChannel client = null;
if (selectionKey.isConnectable()) {
// 強轉成 有鏈接事件發生的Channel
client = (SocketChannel) selectionKey.channel();
// 完成鏈接
if (client.isConnectionPending()) {
client.finishConnect();
ByteBuffer byteBuffer = ByteBuffer.allocate(512);
byteBuffer.put((LocalDate.now() + "鏈接成功").getBytes());
byteBuffer.flip();
client.write(byteBuffer);
}
}
複製代碼