天天看點

關于tcp協定可靠性的個人了解

1、首先,因為tcp協定是可靠的,是以不存在丢包問題,也不存在包順序錯亂問題(udp會存在這個問題,這個時候需要自己使用序号之類的機制保證了,這裡隻讨論tcp)。

2、tcp傳輸會有粘包的問題,一般的做法是先收取一個固定大小的標頭資訊,接着根據標頭裡面指定的包體大小來收取包體大小(這裡“收取”既可以從socket上收取,也可以在已經收取的資料緩沖區裡面拿取)。

3、丢包/粘包/拆包問題

如果TCP通信中發現缺少資料或者丢包,那麼,最大的可能在于程式發送的過程或者接收的過程出現問題。

 例如伺服器給用戶端發大量資料,Send的頻率很高,那麼就有可能在Send時發生錯誤(原因可能是又多種,可能是程式處理邏輯問題,多線程同步問題,緩沖區溢出問題等等),如果沒有對Send失敗做處理重發資料,那麼用戶端收到的資料就會比理論應該收到的少,就會造成丢資料,丢包的現象。

 這種現象,其實本質上來說不是丢包,也不是丢資料,隻是因為程式處理有錯誤,導緻有些資料沒有成功地被socket發送出去。

 常用的解決方法如下:拆包、加標頭、發送,組合包,如果用戶端、服務端掉線,常采用心跳測試。

tcp是一個“流”的協定,一個完整的包可能會被TCP拆分成多個包進行發送,也可能把小的封裝成一個大的資料包發送,這就是所謂的TCP粘包和拆包問題。

粘包、拆包問題說明

假設用戶端分别發送資料包D1和D2給服務端,由于服務端一次性讀取到的位元組數是不确定的,是以可能存在以下4種情況。

(1)服務端分2次讀取到了兩個獨立的包,分别是D1,D2,沒有粘包和拆包;

(2)服務端一次性接收了兩個包,D1和D2粘在一起了,被成為TCP粘包;

(3)服務端分2次讀取到了兩個資料包,第一次讀取到了完整的D1和D2包的部分内容,第二次讀取到了D2包的剩餘内容,這被稱為拆包;

(4)服務端分2次讀取到了兩個資料包,第一次讀取到了部分D1,第二次讀取D1剩餘的部分和完整的D2包;

如果此時服務端TCP接收滑動窗非常小,而資料包D1和D2都很大,很有可能發送第五種可能,即服務端多次才能把D1和D2接收完全,期間多次發生拆包情況。(TCP接收滑動窗:是接收端的大小,随着流量大小而變化,如果我的解釋還不明确,請讀者自行百度,或者查閱《計算機網絡》、《TCP/IP》中TCP的内容)

粘包問題的解決政策

由于底層的TCP無法了解上層的業務邏輯,是以在底層是無法確定資料包不被拆分和重組的,這個問題隻能通過上層的應用協定棧設計來解決,根據業界的主流協定的解決方案,歸納如下:

(1)消息定長,例如每個封包的大小為固定長度200位元組,如果不夠,空位補空格;

(2)在包尾增加回車換行符進行分割,例如FTP協定;

(3)将消息分為消息頭和消息體,消息頭中包含表示消息總長度(或者消息體長度)的字段,通常設計思路是消息頭的第一個字段用int來表示消息的總長度;(我之前linux C開發,就用的這種)。

(4)更複雜的應用層協定;

4、tcp隻要發送成功,就能推出接收成功嗎?即調用tcp的send函數,傳回如果不是socket_error,就證明發送成功?這樣我不用觀察接收端,就能推理出接收成功了嗎?

先來看看原理:

發送方:調用send函數的時候,做的操作實際上是把你給出的資料拷貝的系統的緩存中,然後等待系統發送,send函數的傳回值就是實際拷貝進入系統緩存中的資料的大小,這個大小有可能小于你給定的資料大小,是以可能需要多次調用。

接收方:調用recv函數的時候,和send很類似,是把系統緩存中已經接收到的資料,拷貝到你給出的緩存中,recv的傳回值就是實際從系統緩存中拷貝出來的資料的大小。在實際的網絡傳輸中,會有一些資料不能及時到達,導緻你recv的時候讀出來的資料大小和send的時候的大小不一緻,是以一般也需要多次調用。

注意事項:socket裡面還可以使用MSG_WAITALL标志來等待所有資料到達。

當應用程式調用Send之後怎麼判斷對方是否成功接收?

TCP 的 ACK 表示對方的協定棧已經收到了你發的資料,不代表對方的應用程式收到了你發的消息。對方的應用程式可能死鎖或者阻塞,不會去調用 recv(),那麼你發的資料就堆積在對方協定棧的接收緩沖區裡了。

于是乎,我們的答案是no.因為這個隻是放到發送緩沖區成功了而已~~

(1)send成功,隻是代表資料已經發到發送緩存中,并不代表發送到目的地,發送緩存的資料由網絡層協定來保證發送到目的端,tcp是面向連接配接的,全雙工模式。一般來說,都會成功。

(2)網絡層協定把資料成功發送到目的位址,隻是把資料放到接收端的接收緩存中,接收端沒有recv的話,資料還是沒有成功被接收。不停是send,接收緩存會用完。

(3)接收緩存滿後,網絡層就不會再發送資料過來。這樣當你再次send的時候,就隻是把資料放到發送緩存而已,發送緩存滿後,則send函數會失敗。

(4)如果緩沖區大,放到緩沖區,如果是0,那麼在路上,但一般都不是0。

(5)放到緩沖區和路上不代表對方收到了

(6)如果網絡一切正常,網線沒有掉、到對方的路由沒有問題,TCP會確定資料在某個時候到達對方,即使丢失了,它也會重傳,這是TCP必須保證的。

(7)TCP的ACK是在協定棧實作的,應用層不知道。

(8)如果這個時候程式退出,對方不一定收到資料。

5、Linux多線程服務端程式設計:使用muduo C++網絡庫

關于tcp協定可靠性的個人了解

6、如果用戶端tcp連接配接伺服器,突然把網線拔掉,然後再插回去,這時的socket連接配接的仍舊存在的。目前前提是時間足夠短,1~2秒内網線插回去。時間長了就不行,socket會斷開。

繼續閱讀