天天看點

Socket高可靠性資料傳輸

在計算機網絡中,TCP/IP保證了資料的可靠性傳輸,但是該可靠性傳輸時建立在鍊路可用的情況下的,也就是說在鍊路可用的情況下,該協定可用保證資料可靠的傳輸到對端。

socket就是在TCP/IP協定(當然還包含其他協定)之上的更高層次的系統API,socket底層使用了TCP/IP來保證了鍊路在可用的情況下,資料可靠的傳輸到對端,那麼應用層在使用socket的時候讀寫資料的時候就一定能保證資料可靠的傳輸了嗎?答案是:否,為什麼會這樣?下面将通過分析socket的實作機制來進行相應的說明。

我們通過對linux系統中的socket的資料流傳輸機制的分析來說明為什麼直接使用socket機制來傳輸資料是不可靠的。socket的使用相信大家都不會陌生,服務端監聽端口,用戶端指定端口連接配接到伺服器端,然後就可以進行資料的傳輸了,但是卻很少關系我們在調用socket的send方法之後,我們的資料是怎麼樣被發送到對端的?

在了解資料傳輸之前,需要去了解一下linux的使用者态和核心态的概念。

socket在調用send方法之後,使用者空間的資料會被拷貝到核心空間的socket的緩沖區(可以同socket的OPTION設定)(如果使用DMA可以減少該過程)(核心緩沖區是核心為socket配置設定的記憶體,用于緩存需要發送的資料),而且可以通過設定TCP_NODLAY來設定是否即時發送核心緩沖區的資料到對端(預設是關閉的,至于為什麼關閉這個網上有很多文章說明,最主要是提高資料有效載荷)。如果在使用socket進行資料傳輸的時候沒有設定該選項,那麼就會出現一個問題,應用層多個業務多次send之後,那幾次調用發送的資料可能僅僅是被存放在核心的緩沖區,而沒有被立刻發送到對端,但是應用層的卻認為發送成功,然後使用了應用層的緩沖區,在這個時候如果出現網絡連接配接斷開,那麼核心中已經被緩存的資料将丢失,而且應用層無法知道哪些資料發送失敗了,這種情況就需要應用層自己設計一個可靠的傳輸機制,對于每一個發送出去的request包都對于一個應用層的一個response包,對沒有收到response的包,預設是失敗了,在斷線重連之後重發。

上述的斷線重連消息重發會存在伺服器端重複處理同一個請求多次的問題,這個問題可以通過在協定中設定一個消息ID和type标記,type用于标記該消息是否是重發消息,ID是消息的唯一标記,通過這兩個辨別符,伺服器端首先檢查type是否為重發消息,如果為重發消息就去已處理的消息ID的容器中去找,如果找到那麼就傳回用戶端“該消息已被處理”,如果沒有找到那麼就重新處理消息,并将消息ID加入消息容器中。

下面推薦一篇講述socket資料傳輸機制的文檔,裡面有講述資料從使用者态到核心态的過程。

https://www.ibm.com/developerworks/linux/library/j-zerocopy/