TCP/IP 協議是如何保證數據可靠性的?


原文: 網絡基礎:TCP協議-如何保證傳輸可靠性

 

TCP協議傳輸的特點主要就是面向字節流、傳輸可靠、面向連接。這篇博客,我們就重點討論一下TCP協議如何確保傳輸的可靠性的。

 

確保傳輸可靠性的方式
TCP協議保證數據傳輸可靠性的方式主要有:

  1: 校驗和
  2: 序列號
  3: 確認應答
  4: 超時重傳
  5: 連接管理
  6: 流量控制
  7: 擁塞控制

 

校驗和
計算方式:在數據傳輸的過程中,將發送的數據段都當做一個16位的整數。將這些整數加起來。並且前面的進位不能丟棄,補在后面,最后取反,得到校驗和。
發送方:在發送數據之前計算檢驗和,並進行校驗和的填充。
接收方:收到數據后,對數據以同樣的方式進行計算,求出校驗和,與發送方的進行比對。

注意:如果接收方比對校驗和與發送方不一致,那么數據一定傳輸有誤。但是如果接收方比對校驗和與發送方一致,數據不一定傳輸成功

 

確認應答與序列號
序列號:TCP傳輸時將每個字節的數據都進行了編號,這就是序列號。
確認應答:TCP傳輸的過程中,每次接收方收到數據后,都會對傳輸方進行確認應答。也就是發送ACK報文。這個ACK報文當中帶有對應的確認序列號,告訴發送方,接收到了哪些數據,下一次的數據從哪里發。

 

 序列號的作用不僅僅是應答的作用,有了序列號能夠將接收到的數據根據序列號排序,並且去掉重復序列號的數據。這也是TCP傳輸可靠性的保證之一。

 

超時重傳
  在進行TCP傳輸時,由於確認應答與序列號機制,也就是說發送方發送一部分數據后,都會等待接收方發送的ACK報文,並解析ACK報文,判斷數據是否傳輸成功。如果發送方發送完數據后,遲遲沒有等到接收方的ACK報文,這該怎么辦呢?而沒有收到ACK報文的原因可能是什么呢?

首先,發送方沒有介紹到響應的ACK報文原因可能有兩點:

  1:數據在傳輸過程中由於網絡原因等直接全體丟包,接收方根本沒有接收到。
  2:接收方接收到了響應的數據,但是發送的ACK報文響應卻由於網絡原因丟包了。
  

 TCP在解決這個問題的時候引入了一個新的機制,叫做超時重傳機制。簡單理解就是發送方在發送完數據后等待一個時間,時間到達沒有接收到ACK報文,那么對剛才發送的數據進行重新發送。如果是剛才第一個原因,接收方收到二次重發的數據后,便進行ACK應答。如果是第二個原因,接收方發現接收的數據已存在(判斷存在的根據就是序列號,所以上面說序列號還有去除重復數據的作用),那么直接丟棄,仍舊發送ACK應答。

 那么發送方發送完畢后等待的時間是多少呢?如果這個等待的時間過長,那么會影響TCP傳輸的整體效率,如果等待時間過短,又會導致頻繁的發送重復的包。如何權衡?

 由於TCP傳輸時保證能夠在任何環境下都有一個高性能的通信,因此這個最大超時時間(也就是等待的時間)是動態計算的。

在Linux中(BSD Unix和Windows下也是這樣)超時以500ms為一個單位進行控制,每次判定超時重發的超時時間都是500ms的整數倍。重發一次后,仍未響應,那么等待2*500ms的時間后,再次重傳。等待4*500ms的時間繼續重傳。以一個指數的形式增長。累計到一定的重傳次數,TCP就認為網絡或者對端出現異常,強制關閉連接。

 

連接管理
  連接管理就是三次握手與四次揮手的過程,在前面詳細講過這個過程,這里不再贅述。保證可靠的連接,是保證可靠性的前提。

 

流量控制
  接收端在接收到數據后,對其進行處理。如果發送端的發送速度太快,導致接收端的結束緩沖區很快的填充滿了。此時如果發送端仍舊發送數據,那么接下來發送的數據都會丟包,繼而導致丟包的一系列連鎖反應,超時重傳呀什么的。而TCP根據接收端對數據的處理能力,決定發送端的發送速度,這個機制就是流量控制。

  在TCP協議的報頭信息當中,有一個16位字段的窗口大小。在介紹這個窗口大小時我們知道,窗口大小的內容實際上是接收端接收數據緩沖區的剩余大小。這個數字越大,證明接收端接收緩沖區的剩余空間越大,網絡的吞吐量越大。接收端會在確認應答發送ACK報文時,將自己的即時窗口大小填入,並跟隨ACK報文一起發送過去。而發送方根據ACK報文里的窗口大小的值的改變進而改變自己的發送速度。如果接收到窗口大小的值為0,那么發送方將停止發送數據。並定期的向接收端發送窗口探測數據段,讓接收端把窗口大小告訴發送端。

 注:16位的窗口大小最大能表示65535個字節(64K),但是TCP的窗口大小最大並不是64K。在TCP首部中40個字節的選項中還包含了一個窗口擴大因子M,實際的窗口大小就是16為窗口字段的值左移M位。每移一位,擴大兩倍。

擁塞控制
  TCP傳輸的過程中,發送端開始發送數據的時候,如果剛開始就發送大量的數據,那么就可能造成一些問題。網絡可能在開始的時候就很擁堵,如果給網絡中在扔出大量數據,那么這個擁堵就會加劇。擁堵的加劇就會產生大量的丟包,就對大量的超時重傳,嚴重影響傳輸。

  所以TCP引入了慢啟動的機制,在開始發送數據時,先發送少量的數據探路。探清當前的網絡狀態如何,再決定多大的速度進行傳輸。這時候就引入一個叫做擁塞窗口的概念。發送剛開始定義擁塞窗口為 1,每次收到ACK應答,擁塞窗口加 1。在發送數據之前,首先將擁塞窗口與接收端反饋的窗口大小比對,取較小的值作為實際發送的窗口。

  擁塞窗口的增長是指數級別的。慢啟動的機制只是說明在開始的時候發送的少,發送的慢,但是增長的速度是非常快的。為了控制擁塞窗口的增長,不能使擁塞窗口單純的加倍,設置一個擁塞窗口的閾值,當擁塞窗口大小超過閾值時,不能再按照指數來增長,而是線性的增長。在慢啟動開始的時候,慢啟動的閾值等於窗口的最大值,一旦造成網絡擁塞,發生超時重傳時,慢啟動的閾值會為原來的一半(這里的原來指的是發生網絡擁塞時擁塞窗口的大小),同時擁塞窗口重置為 1。

 擁塞控制是TCP在傳輸時盡可能快的將數據傳輸,並且避免擁塞造成的一系列問題。是可靠性的保證,同時也是維護了傳輸的高效性。

 

 附: 數據報的IP校驗和計算方法?

 


首先,題主問題里這段話,應該來自《TCP/IP詳解卷一:協議》第一版的翻譯版第三章。
為了計算一份數據報的IP檢驗和,首先把檢驗和字段置為0。然后,對首部中每個16bit進行二進制反碼求和(整個首部看成是由一串16bit的字組成),結果存在檢驗和字段中。當收到一份IP數據報后,同樣對首部中每個16bit進行二進制反碼的求和。由於接受方在計算過程中包含了發送方存在首部中的校驗和。因此,如果首部在傳輸過程中沒有發生任何差錯,那么接受方計算的結果應該為全1。如果結果不是全1(即檢驗和錯誤),那么IP就丟棄收到的數據報。
原文是這樣的:
To compute the IP checksum for an outgoing datagram, the value of the checksum field is first
set to 0. Then the 16-bit one's complement sum of the header is calculated(i.e,the entire header is considered a sequence of 16-bit words). The 16-bit one's complement of this sum is stored in the checksum field. When an IP datagram is received, the 16-bit one's complement sum of the header is calculated. Since the receiver's calculated checksum contains the checksum stored by the sender, the receiver's checksum is all one bits if nothing in the header
was modified. If the result is not all one bits(a checksum error), IP discards the received datagram. No error message is generated. It is up to the higher layers to somehow detect the missing datagram and retransmit.

在讀懂這段話之前,首先要了解究竟什么是Ones' complement。簡單的說就是正數為其原碼,負數其同絕對值對應正數所有bit取相反值。比如1的one's complement就是0000 0001,-1的one's complement 就是1111 1110 。+0的one's complement所有bit都是0, -0的one's complement所有bit都是1。

下面是一個簡單的例子。
發送前,假設IP頭部20字節消息如下:
12 34  56 78  9A BC  DE F0  12 34  00 00  9A BC  DE F0  12 34  56 78

其中校驗和部分被暫時先置為00 00。
把這部分字節流按每兩個字節為一單元分割,然后對分割后的所有兩字節數進行二進制求和:
0x1234 + 0x5678 + 0x9ABC + 0xDEF0 + 0x1234 + 0x0000 + 0x9ABC +0xDEF0 + 0x1234 + 0x5678
按照32位數的加法得到0x3D6E4, 但我們的校驗和是16位的,需要持續把這個結果的高16位加回到低16位,直到高16位為0,我們再次計算
0x0003 + 0xD6E4
得到結果為0xD6E7。這次高16位已經為0了。
最后我們對0xD6E7按位取反,
1101 0110 1110 0111  ->  0010 1001 0001 1000
得到校驗和 = ~0xDE67 = 0x2918。最后我們發送的IP頭部字節流為:
12 34  56 78  9A BC  DE F0  12 34  29 18  9A BC  DE F0  12 34  56 78

接收方收到IP頭部后,依舊把這部分消息按每兩個字節為一單元分割,同樣再對分割后的所有二字節數(包括校驗和自身所在的字節)進行二進制求和。這個時候計算
0x1234 + 0x5678 + 0x9ABC + 0xDEF0 + 0x1234 + 0x2918 + 0x9ABC +0xDEF0 + 0x1234 + 0x5678
得到結果為0x3FFFC,繼續計算
0x0003 + 0xFFFC
得到0xFFFF,即所有位數都是1,這也是-0的one's complement。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM