NIO服務(wù)端通信序列圖如下:

下面,我們對(duì)NIO服務(wù)端的主要?jiǎng)?chuàng)建過(guò)程進(jìn)行講解和說(shuō)明:
步驟一:打開(kāi)ServerSocketChannel,用于監(jiān)聽(tīng)客戶(hù)端的連接,它是所有客戶(hù)端連接的父管道,代碼示例如下:
ServerSocketChannel acceptorSvr = ServerSocketChannel.open();
步驟二:綁定監(jiān)聽(tīng)端口,設(shè)置連接為非阻塞模式,示例代碼如下:
acceptorSvr.socket().bind(new InetSocketAddress(InetAddress.getByName("IP"),port));
acceptorSvr.configureBlocking(false);
步驟三:創(chuàng)建Reactor線程,創(chuàng)建多路復(fù)用器并啟動(dòng)線程,代碼如下:
Selector selector = Selector.open();
New Thread(new ReactorTask()).start();
步驟四:將ServerSocketChannel注冊(cè)到Reactor線程的多路復(fù)用器Selector上,監(jiān)聽(tīng)ACCEPT事件,代碼如下:
SelectionKey key = acceptorSvr.register(selector,SelectionKey.OP_ACCEPT,ioHandler);
步驟五:多路復(fù)用器在線程run方法的無(wú)限循環(huán)體內(nèi)輪詢(xún)準(zhǔn)備就緒的Key,代碼如下:
int num = selector.select();
Set selectedKey s = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while(it.hasNext()){
SelectionKey key = (SelectionKey)it.next();
// ... deal with I/O event ...
}
步驟六:多路復(fù)用器監(jiān)聽(tīng)到有新的客戶(hù)端接入,處理新的接入請(qǐng)求,完成TCP三次握手,建立物理鏈路,代碼示例如下:
SocketChannel channel = svrChannel.accept();
步驟七:設(shè)置客戶(hù)端鏈路為非阻塞模式,示例代碼如下:
channel.configureBlocking(false);
channel.socket().setReuseAddress(true);
......
步驟八:將新接入的客戶(hù)端連接注冊(cè)到Reactor線程的多路復(fù)用器上,監(jiān)聽(tīng)讀操作,用來(lái)讀取客戶(hù)端發(fā)送的網(wǎng)絡(luò)消息,代碼如下:
SelectionKey key = socketChannel.register(selector,SelectionKey.OP_READ);
步驟九:異步讀取客戶(hù)端請(qǐng)求消息到緩沖區(qū),示例代碼如下:
int readNumber = channel.read(recelvedBuffer);
步驟十:對(duì)ByteBuffer進(jìn)行編解碼,如果有半包消息指針reset,繼續(xù)讀取后續(xù)的報(bào)文,將解碼成功的消息封裝成Task,投遞到業(yè)務(wù)線程池中,進(jìn)行業(yè)務(wù)邏輯編排,示例代碼如下:
Object message = null;
while(buffer.hasRemain()){
byteBuffer.mark();
Object message = decode(byteBuffer);
if(message == null){
byteBuffer.reset();
break;
}
messageList.add(message);
}
if(!byteBuffer.hasRemain())
byteBuffer.clear();
else
byteBuffer.compact();
if(messageList != null & !messageList.isEmpty()){
for(Object messageE :messageList)
handlerTask(messageE);
}
步驟十一:將POJO對(duì)象encode成ByteBuffer,調(diào)用SocketChannel的異步write方法,將消息異步發(fā)送給客戶(hù)端,示例代碼如下:
socketChannel.write(buffer);
注意:如果發(fā)送區(qū)TCP緩沖區(qū)滿(mǎn),會(huì)導(dǎo)致寫(xiě)半包,此時(shí),需要注冊(cè)監(jiān)聽(tīng)寫(xiě)操作位,循環(huán)寫(xiě),直到整包消息寫(xiě)入TCP緩沖區(qū)。