Server端如何感知客戶端的狀態
如果網路擁塞嚴重,chatserver端如何感知客戶端在線還是掉線了?
客戶端主動發送close(fd),相當于TCP的四次揮手,發送FIN包,進行揮手操作,對應服務端,就有一個回應:recv=0(判斷客戶端掉線,下線了),
這是我們在局域網的聊天服務器里面,網路是良好的,是不會出現問題的,
但是,如果放在真實的網路環境中,這個客戶端發送的FIN包有可能服務器根本收不到,因為真實的網路環境是非常非常復雜的,有可能現在我們的客戶端到服務端的中間網路節點的路由器里面的報文非常多,網路擁塞非常嚴重,導致FIN包到達不了服務端,
TCP協議下,發送的每一個包都會去等待這個包的回應,如果沒有回應,會超時重傳,但是超時重傳是有一定的次數,如果超過次數,TCP就會reset把socket重置了,但是還是導致這個FIN包最侄訓是沒有到達服務端,導致這個客戶端已經下線了,但是服務端并沒有感知到,客戶端已經自己重置了,服務端卻一直以為這個客戶端在線,把這個客戶端所給的socketfd沒有釋放,相關的其他資源也沒有釋放,就積累越來越多的僵客戶端連接,
解決方法
心跳機制!!!
比如說,服務端(listen 8080),是TCP的埠,當然,不同的協議可以系結在同一個埠,listen 8080是專門處理業務埠,接收我們發的json資料處理業務,
UDP 8080 是心跳業務處理

可以這樣設計,服務端會給每一個connect成功的客戶端分配1個心跳計數:
比如說:

服務端會啟動一個通用定時器(心跳計數器),比如說:超時1秒,把所有客戶的心跳計數加1,每超過1秒,就加1
如果客戶的心跳計數超過5了,就判斷客戶端已經掉線了,
每一個客戶端每隔1秒都會去發了一個心跳訊息,給所屬的心跳計數減1
也就是說,服務端在監聽1個客戶端,5秒之內都沒有任何心跳的話,就判斷這個客戶端下線了,就拆除這個客戶端所有的連接以及其他資源,
客戶端都是通過TCPsocket來和服務端進行通信,心跳為了不干擾我們的業務處理,專門用UDPsocket系結8080埠,或者我們可以系結其他的心跳埠,專門接收心跳訊息,
當然,客戶端發送的心跳訊息要包括:userid:zhangsan1MessageType:heartbeat,服務端接收到這個心跳訊息,就把zhangsan1對應的心跳計數減1,以此類推,正常,客戶端的對應的心跳計數應該在-1,0,1之間不斷的變化
如果客戶端實時的發送心跳訊息,服務端維護的這個客戶端的心跳計數是不會超過5的,這樣的機制可以預防網路擁塞嚴重,客戶端的訊息已經無法到達服務端了,這個訊息自然也就無法到達服務端,這樣一來,服務端維護的相應客戶端的心跳計數,每超過1秒,沒有收到心跳訊息,就給心跳計數加1,加到超過5了,就認為這個客戶端掉線了,說明此時的網路情況是非常非常的差,可以認為客戶端不在線,因為客戶端的訊息過不來服務端,也有可能是發生了網路風暴,導致訊息環回,到不了服務端了,

一般來說,C/S這種基于長連接的業務,都會在業務層實作心跳保持機制,用于監測對端是否依然在線
我們上面所說的是在應用層業務上自己實作的心跳機制,
在傳輸層,TCP協議有一個keepalive功能,保活功能,
因為TCP協議有一個缺陷,所以需要加上這個功能來彌補它的缺陷,
這個缺陷是:
TCP客戶端和服務端連接成功了以后,如果客戶端和服務端不互動,不發資訊的話,一直連接鏈路,保持空閑,那么對于服務端來說,到底怎么去理解客戶端的狀態呢?客戶端是在線,連接一直建立著,是客戶端一直沒有發訊息,還是客戶端掉線了?由于網路問題,客戶端沒有告訴服務端,服務端得不到通知,就像我們打電話一樣,突然,都保持沉默了,你也不知道對方是掉線了還是沒有說話,對于服務端來說,沒有辦法去感知所謂的事件創建的連接,當它里面一直沉默的話,到底這個連接還是不是有效的,
所以,TCP協議里,增加了keepalive這個功能:是在傳輸層添加的這個功能,和應用層沒有關系,
有一些引數:
keepalive功能默認是關閉的,當我們去創建TCP的listen socket的時候,在這里,如果我們想啟動keepalive功能,我們需要通過setsockopt來打開它的SO_KEEPALIVE,
它做的事情是:

net.ipv4.tcp_keepalive_time=7200:默認每隔2個小時,會發送一個空的報文段,探測對方是否在線:如果對方回復了,證明對方還在線,鏈路還是有效的,如果對方并沒有回應,
net.ipv4.tcp_keepalive_invl=75:如果探測沒有回應,延遲75秒繼續發送保活的探測包,
net.ipv4.tcp_keepalive_probes=9:如果依舊是沒有回應的話,最多重新探測9次,如果都沒有回應,就拆除連接!!!證明這個連接確實是掛掉了,
也就是最多等待2小時+75x9 秒,如果一直沒有回應,就把連接給拆除了
這個引數是可以更改的,可以把時間調小一點,
如果我們是靠TCP的keepalive來實作保活功能,在這里,如果連接中斷的話,因為這是在傳輸層,只能把TCP的連接拆除,但是我們在應用層,一個聊天服務端檢測到一個聊天客戶端下線,僅僅是關閉sockfd???在我們的業務上,連接還記錄著對應的connection物件存盤在map表,我們都得洗掉啊!!!其次,因為這個keepalive是在傳輸層的,傳輸層的上面才是我們的應用層,假設我們應用層已經運行到死鎖了,出問題了,已經做不了業務了,但是這只是應用層的死鎖,傳輸層是屬于作業系統內核的,內核是不會死鎖的,socket在內核中依然可以和服務端發送keepalive探測包的,這就出問題了,導致服務端還是認為客戶端在線的!!!
長連接的C/S是不可能依靠TCP傳輸層的keepalive功能做連接保活機制的,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/307200.html
標籤:其他
