NIO客戶端時序圖 如下:
步驟一:打開SocketChannel,綁定客戶端本地地址(可選,默認(rèn)系統(tǒng)會隨機(jī)分配一個可用的本地地址),示例代碼如下:
SocketChannel clientChannel = SocketChannel.open();
步驟二:設(shè)置SocketChannel為非阻塞模式,同時設(shè)置客戶端連接的TCP參數(shù),示例代碼如下:
clientChannel.configureBlocking(false);
socket.setReuseAddress(true);
socket.setReceiveBufferSize(BUFFER_SIZE);
socket.setSendBufferSize(BUFFER_SIZE);
步驟三:異步連接服務(wù)端,示例代碼如下:
boolean connected = clientChannel.connect(new InetSocketAddress(“ip”,port));
步驟四:判斷是否連接成功,如果連接成功,則直接注冊讀狀態(tài)位到多路復(fù)用器中,如果當(dāng)前沒有連接成功(異步連接,返回false,說明客戶端已經(jīng)發(fā)送sync包,服務(wù)端沒有返回ack包,物理鏈路還沒有建立),示例代碼如下:
if (connected) {
clientChannel.register( selector, SelectionKey.OP_READ, ioHandler);
} else {
clientChannel.register( selector, SelectionKey.OP_CONNECT, ioHandler);
}
步驟五:向Reactor線程的多路復(fù)用器注冊O(shè)P_CONNECT狀態(tài)位,監(jiān)聽服務(wù)端的TCP ACK應(yīng)答,示例代碼如下:
clientChannel.register( selector, SelectionKey.OP_CONNECT, ioHandler);
步驟六:創(chuàng)建Reactor線程,創(chuàng)建多路復(fù)用器并啟動線程,代碼如下:
Selector selector = Selector.open();
New Thread(new ReactorTask()).start();
步驟七:多路復(fù)用器在線程run方法的無限循環(huán)體內(nèi)輪詢準(zhǔn)備就緒的Key,代碼如下:
int num = selector.select();
Set selectedKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while (it.hasNext()) {
SelectedKey key = (SelectedKey) it.next();
}
步驟八:接收 connect事件進(jìn)行處理,代碼如下:
if (key.isConnectable())
//handlerConnect();
步驟九:判斷連接結(jié)果,如果連接成功,注冊讀事件到多路復(fù)用器,示例代碼如下:
if (channel.finishConnect())
registerRead();
步驟十:注冊讀事件到多路復(fù)用器:
clientChannel.register( selector, SelectionKey.OP_READ, ioHandler);
步驟十一:異步讀客戶端請求消息到緩沖區(qū),示例代碼如下:
int readNumber = channel.read(receivedBuffer);
步驟十二:對ByteBuffer進(jìn)行編解碼,如果有半包消息接收緩沖區(qū)Reset,繼續(xù)讀取后續(xù)的報文,將解碼成功的消息封裝成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對象encode成ByteBuffer,調(diào)用SocketChannel的異步write接口,將消息異步發(fā)送給客戶端,示例代碼如下:
socketChannel.write(buffer);