在類似rpc(遠端過程呼叫)場景中為了保證傳輸的效率,通常情況下會採用長鏈結,而長鏈結的保持即通過定時心跳實現。類似場景在訊息推送服務中也是非常常見。所以心跳服務是網路程式設計中的基礎且普遍的應用。
dubbo和zookpper都有心跳飽和機制
由客戶端定時傳送給服務端,服務端作出響應。因為一直要傳送,所以傳送的訊息和返回的訊息的體量必須足夠小,只要能標識心跳事件即可。具體訊息格式設計要根據所使用的應用協議而定。
在客戶端我們採用socketchannel來連線服務並註冊到選擇器,由選擇器來監聽管道的狀態並觸發相對應的事件處理。並通過執行緒休眠來實現,定時傳送。其整個流程如下圖
先初始化初始化管道,建立連線()。
連線遠端服務,在非阻塞模式,connect()是非同步完成的,當服務端accept鏈結,客戶端會觸發op_connect事件,然後必須呼叫 finishconnect() 才能真正完成呼叫。
socketchannel channel = socketchannel.
open()
;// 開啟管道
channel.
configureblocking
(false);
//設定非阻塞模型
selector selector = selector.
open()
;//開啟選擇器
channel.
register
(selector, selectionkey.op_connect)
;//監聽連線事件
註冊選擇器。
選擇器進行迴圈遍歷(基於對擇器的輪詢,就可以獲得選擇集,並觸發相對應事件)。
while
(true)}
}
循序操作首先呼叫select()重新整理鍵,重新整理鍵的時候會有三個事件
當服務端accept連線後,客戶端就會觸發op_connect事件。此時並不代表連線已完成,這時往管道中寫資料是會報notyetconnectedexception異常的。必須呼叫 finishconnect()才會真正建立好連線。當建立連線後,就不在需要監聽 op_connect,而是op_write事件,以將心跳事件資料寫入管道。
if
(key.
isconnectable()
)
什麼時候會觸發op_write 事件?當建立連線後管道就是乙個可寫狀態,所以直接就能觸發op_write事件。並且在關閉連線前op_write事件,一直會被觸發。所以寫入心跳後必須移除對op_write事件的監聽,改為監聽op_read事件。
if
(key.
isconnectable()
)
當服務端訊息返回客戶後就會觸發客戶端的op_read事件,此時直接讀取即可。然後在將執行緒休眠2s後切換監聽到op_write。如果不休眠會立馬觸發op_write事件。
channel.
read
(bytebuffer.
allocate(64
));key.
interestops
(selectionkey.op_write)
;thread.
sleep
(2000);
// 休眠2秒防止立馬進行寫入
因為是客戶端不是服務端沒有accpet
服務端流程說明
初始化管道
與客戶端類似 也是nio中常規操作,開啟管道與選擇器,設定阻塞然後註冊到選擇器。
這裡服務端使用serversocketchannel ,而客戶端使用socketchannel。兩者區別是serversocketchannel僅用於接受連線,不支援讀寫。而socketchannel用於連線和讀寫。
serversocketchannel serverlistener=serversocketchannel.
open()
;serverlistener.
bind
(new
inetsocketaddress
(8080))
;// 繫結tcp埠
serverlistener.
configureblocking
(false);
selector selector = selector.
open()
;// 註冊accept 事件,用於同意客戶端連線
serverlistener.
register
(selector,selectionkey.op_accept)
;
輪詢選擇器
請參照上面選擇器輪詢
觸發op_accept事件
op_accept指有新連線到達,通過serversocketchannel.accept() 即可獲取乙個新管道socketchannel。 基於它就可以與客戶端進行讀寫。這種讀寫同樣基於非阻塞方式執行,並註冊到選擇器。
if
(key.
isacceptable()
)
注:假設服務端接收的連線到達極限,是否可以直接勿略客戶端連線,不執行accept()方法?這是不行的,在tcp中所有事件都必須進行處理,否則會一直觸發該事件,造成死迴圈。
4. 觸發op_read事件
當客戶端傳送資料過來,即會觸發讀取事件,這時直接讀取即可,然後在寫回響應資料即可。
if
(key.
isreadable()
) buffer.
put(string.
valueof
(system.
currenttimemillis()
).getbytes()
);buffer.
flip()
; channel.
write
(buffer)
;// 寫回資料到管道
}
位元組』4』 在ascii中 表示eot (end of transmission )傳輸結束,所以在管道中讀取到4這個位元組,即可手動的去關閉連線。 演算法運用 KMP實戰演練
看完花名,滾來寫總結了。嗚嗚.題目 hdu1358 大意 求字串子串最大分割自我迴圈次數。表達的不好 解題 第一念頭想著用kmp兩層迴圈切割字串,第一層處理原串,第二層處理模式串。然後費了好大的勁寫了出來,結果,結果超時了 喂,這是理所應當的吧 沒辦法只好搜答案,發現只要運用next陣列就可以解決了...
《Python高效開發實戰》實戰演練 建立應用2
為了在專案中開發符合mvc架構的實際應用程式,需要在專案中建立django應用。每個django專案可以包含多個django應用。建立應用的語法為 應用名稱 cd djangosite命令完成後會在專案目錄中建立如下目錄及檔案結構 init py admin.py migrations init p...
iOS JSPatch熱更新之實戰演練
哈哈哈,希望上線的 永無bug,這樣就不用熱更新了。匯入需要的標頭檔案 require uilabel,uiimageview,nsurl,nsstring,uzgpersonalsetting,uiimage,uifont,uiscreen require uicolor defineclass ...