如何保證訊息的可靠傳輸?
意思是:客戶端把訊息發送出去了,只要客戶端這里顯示他的訊息發送成功,就要保證對端一定要收到,要么收不到,客戶端就顯示發送失敗,用戶后續選擇重新發送訊息,如果客戶端顯示訊息發送成功,就一定保證對端一定收到這條訊息,
我們可以在業務層實作訊息的確認機制(結合心跳):在訊息發送之后對端給予回應,
集群聊天服務器是基于TCP協議實作的,TCP協議本身是可靠的傳輸協議,在發送資料的時候,有超時重傳機制,TCP發送每一個資料的時候,都會等待得到接收方回傳的ACK訊息確認,它如果得不到ACK,就會超時重傳,選擇重新發送這個資料,得到ACK確認之后,保證訊息發送成功了,
為什么業務層上還要實作訊息確認機制呢?
我們看看下面這張圖:

這個send的回傳值大于0,發送成功了,回傳了發送成功的位元組數,但是并不代表著這條訊息跑到對端,對端接收到了,
send怎么可能是呼叫的時候還包括客戶端往服務器發送的程序和服務器接收到了,然后回應,回應成功了,send才回傳?不可能的!如果是這樣的話,send呼叫所花費的時間就非常長了,尤其是在網路環境非常復雜的情況下,這樣做,耗費的時間是非常長的,
當我們在行程的用戶空間的資料,就是上圖中的buf,然后它要發送,呼叫系統的介面send,把用戶空間的buf資料拷貝到內核空間的TCP發送緩沖區中,因為TCP是流式服務,有TCP的發送緩沖區和接識訓沖區, UDP資料報協議是沒有發送緩沖區和接識訓沖區的,對于TCP發送來說,send發送成功,僅僅只是代表說把用戶空間的buf里面的多少資料拷貝到內核空間的TCP發送緩沖區中,還沒有把資料發送到對端,

內核空間的TCP發送緩沖區的資料,是由內核的TCP協議堆疊(相當于是TCP協議的代碼模塊),專門把發送緩沖區的資料(根據滑動視窗機制,可以根據網路狀況,調節發送緩沖區的資料的發送的一個快慢,調節流量,把資料發送出去),
send只是把資料發送到TCP的發送緩沖區中,然后send就回傳了,用戶空間繼續向下執行代碼,所以,send成功回傳,并不能說明訊息到達對端了,

內核空間處理TCP發送緩沖區的資料,開啟TCP的傳輸,TCP傳輸的時候,傳1個資料報文,在這里,有可能出現2種情況:
情況1、從C端發送到S端后,由于網路的情況比較復雜,網路比較擁塞,資料報文經常切換網路節點的路由,導致這個資料報文的TTL在沒有到達S端,已經超過上限了,直接被路由器把當前的資料報文丟棄了,到達不了S端了,
情況
情況 2、這個報文到達S端了,基于TCP的可靠傳輸,對每一個發送的資料報文,都要進行回應,這個ACK可能就碰到了情況1了,導致ACK報文在網路中丟了,沒有到達C端,
上面2種情況都是屬于報文傳輸失敗了,

當C端第一次發送報文的時候,TCP協議堆疊里面就會起1個超時重傳定時器,當我們發送的資料報文到達S端,S端回傳的這個ACK也到達C端了,那我們就可以繼續發送下一個報文了,
如果說,當我第一次發送該報文,超時重傳定時器已經超時了,還沒有得到S端的回應ACK,它就會重新去發送這個資料報文,力求這個S端可以回傳這個報文的ACK,如果后面S端回應了這個ACK,說明這個報文傳輸成功,如果還是出現了資料報文沒有發到S端,或者S端回傳的ACK在中途丟了,C端沒有收到,超過定時時間,它又要進行超時重傳,超時重傳是有上限次數的,是不可能無限次超時重傳下去的,網路情況是很復雜的,如果網路情況復雜,搞了很多很多的TCP報文超時重傳,一直不斷重傳,整個內核所占用的資源永遠得不到釋放,這是非常不好的,所以,超時重傳超過一定次數,TCP會發一個RST(連接重置)的報文給到對端,整個超時重傳流程就結束了,TCP的超時重傳是在一定程度上保證了報文的可靠性傳輸,但是如果網路環境比較差,一直重傳都沒有得到回應,TCP發送一個RST連接重置報文就不管了,訊息還是沒有到達S端!!!所以,我們聊天訊息的可靠傳輸肯定是不能靠協議來保證的,
**內核空間是不可能給用戶提供一個回呼函式提醒訊息沒有傳輸成功的,**尤其是在TCP協議堆疊,TCP協議堆疊是負責網路模塊的,其本身就隨著網路環境的復雜,TCP協議堆疊本身實作也很復雜,如果在這里給用戶注冊一個回呼函式,萬一回呼函式里訪問了一個空指標,直接把內核的TCP模塊廢掉了,怎么辦?
Linux的信號signal,信號的回呼操作內核發給你當前行程的信號,可以寫代碼讓這個信號掛掉,因為信號是某個行程的信號,把行程搞掛掉,就是讓這個行程不要運行了而已,但是TCP協議堆疊是給的用戶空間的所有應用程式服務的,萬一給你注冊回呼,把內核掛了,怎么辦???
資料傳輸的程序:傳輸層(TCP)-》網路層(IP)-》資料鏈路層(MAC)
網路層以上是通過IP地址定位主機的,網路層以下(從鏈路層開始)是通過Mac2地址定位主機的,Mac是真真正正,把資料一幀一幀的發送到網路上了,Mac發送一幀的大小:上限是MTU 1500位元組,TCP的報頭是20位元組,IP的報頭是20位元組,一般來說,一個網卡的一幀最多攜帶1500-40=1460位元組資料,如果我們發送的資料的大小是超過1460位元組的,我們在IP層可以進行分包,分片傳輸,分成1小片1小片,發送到對后,可以把這些小的分片組成原始的資料,
正確方法
我們還是得在業務層上實作訊息的可靠傳輸!!!
我們把客戶端要發送的訊息都快取起來,因為客戶端要實作訊息的可靠傳輸,而且,每一條訊息都有seq序列號,不管是一對一聊天,還是群聊,每一個物件在每一個聊天的會話都有訊息的序列號seq,
客戶端A連續發送3條訊息:
message1 seq:0
message2 seq:1
message3 seq:2
0號訊息先發送出去,正常情況下,這條訊息發送出去以后,客戶端要等待服務端對這個訊息的回應ACK,然后從正常流程來說,客戶端發送1號訊息,
以此類推,
或者說,在客戶端中,也沒有必要按照順序發送訊息,為了性能來說,可以把訊息全部發送出去,因為每條訊息都有seq序列號,在對端收到訊息以后,會對訊息進行快取,按順序顯示訊息,所以發送方可以一次性全部發送出去,但是發送方一定要等待每個訊息回應的ACK,哪個訊息回應了ACK,就說明哪個訊息是可靠傳輸到了對端(因為訊息都有序列號,可以區分出來),客戶端發送到服務器,服務器轉發到另一個客戶端上,都是根據這個方法,如果客戶端收到某條訊息的ACK了,就可以把這條訊息從本地快取洗掉了,說明這個訊息后面不用進行重傳了,如果客戶端得不到對1號訊息的ACK的話,我們可以在客戶端設定超時重傳機制,如果3毫秒沒有收到這條訊息的ACK,再重新發這條訊息,以此類推,如果重復傳3次還沒有收到這條訊息的ACK,我們就給客戶端顯示:該訊息發送失敗,等下一次網路恢復,由用戶來選擇是否重傳,

客戶端也可以通過實作心跳機制來檢查服務端的連接能不能連通,心跳正常,發送訊息肯定沒問題,比如說,客戶端隔1秒發送一次心跳,如果服務端沒有回應,客戶端的心跳計數就從0加到1,如果收到服務端的回應,就減1,以此類推,如果心跳計數超過3,客戶端就判定和服務端的連接斷開了,客戶端就是紅色狀態了,如果客戶端沒有退出APP的話,就一直發送心跳,如果發著發著,網路恢復了,客戶端發了一個心跳,服務端回應了,客戶端把心跳計數的3改為0,繼續發送心跳,如果付訓復了,現在又出現問題了,心跳計數又超過3了,又判定和服務端連接有問題了
也就是說,心跳給了和服務端的連接狀態,我們發送訊息的時候可以先看連接的狀態,如果是OK的話,心跳訊息都能互動成功,我們發送普通的訊息肯定也可以收到ACK回應的,如果心跳已經是失敗的,我們在全域有一個狀態(和服務器的狀態)已經不可用的狀態(inactive),我們客戶端在發送訊息就直接是不能發送,直接給用戶顯示發送失敗,不用嘗試發送了,心跳都失敗,發送普通訊息還能成功???等下一次心跳恢復正常的話,我們再去發送訊息,
我們在TCP,UDP都會設定心跳機制來復雜的網路環境中,客戶端檢測服務端鏈路是否正常,服務端檢測客戶端是否在線,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/307766.html
標籤:其他
