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