前言
本筆記記錄 TCP/IP 中的 TCP 理論。包括三次握手、四次揮手、狀態變遷、慢啟動、快重傳等等。
《TCP/IP詳解》一共三卷,其中卷二、卷三更多偏重於程式設計細節,而卷一更多偏重於基礎原理。
後面再發布個支援處理多執行緒併發及客戶端數量限制的TCP服務端+TCP客戶端例程。
原文:https://www.cnblogs.com/lizhuming/p/14916605.html
17. TCP 傳輸控制協議
17.1 引言
17.2 TCP 服務
TCP提供一種面向連線的、可靠的位元組流服務。
TCP通過下列方式來提供可靠性:
- 應用資料被分割成TCP認為最適合傳送的資料塊。由 TCP傳遞給IP的資訊單位稱為報文段或段(segment)。
- 當TCP發出一個段後,它啟動一個定時器,等待目的端確認收到這個報文段。如果不能及時收到一個確認,將重發這個報文段。
- 當TCP收到發自TCP連線另一端的資料,它將傳送一個確認。這個確認不是立即傳送,通常將推遲幾分之一秒。
- TCP將保持它首部和資料的檢驗和。這是一個端到端的檢驗和,目的是檢測資料在傳輸過程中的任何變化。如果收到段的檢驗和有差錯, TCP將丟棄這個報文段和不確認收到此報文段(希望發端超時並重發)。
- TCP報文段作為 IP 資料包來傳輸,而IP資料包的到達可能會失序,因此TCP報文段
的到達也可能會失序。如果必要,TCP將對收到的資料進行重新排序,將收到的資料以
正確的順序交給應用層。 - IP資料包會發生重複,TCP的接收端必須丟棄重複的資料。
- TCP還能提供流量控制。TCP連線的每一方都有固定大小的緩衝空間。 TCP的接收端只允許另一端傳送 接收端緩衝區 所能接納的資料。(視窗)
位元組流服務(byte stream service):
- 兩個應用程式通過TCP連線交換8 bit位元組構成的位元組流。TCP不在位元組流中插入記錄識別符號。
17.3 TCP的首部
檢視:
-
埠:每個TCP段都包含源端和目的端的埠號,用於尋找發端和收端應用程式。這兩個值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個TCP連線。
- socket:包含客戶 IP 地址、客戶埠號、伺服器 IP 地址和伺服器埠號的四元組。
-
序號:用於對位元組流進行編號。
- 例如序號為 301,表示第一個位元組的編號為 301,如果攜帶的資料長度為 100 位元組,那麼下一個報文段的序號應為 401。
-
確認號:期望收到的下一個報文段的序號。
- 例如 B 正確收到 A 傳送來的一個報文段,序號為 501,攜帶的資料長度為 200 位元組,因此 B 期望下一個報文段的序號為 701,B 傳送給 A的確認報文段中確認號就為 701。
-
資料偏移:指的是資料部分距離報文段起始處的偏移量,實際上指的是首部的長度。
-
URG:緊急(The urgent pointer) 標誌置位。只有當 URG 標誌置1時緊急指標才有效。
-
ACK:確認標誌。當 ACK=1 時確認號欄位有效,否則無效。
- TCP 規定,在連線建立後所有傳送的報文段都必須把 ACK 置 1。
-
PSH:推標誌。該標誌置位時,接收端不將該資料進行佇列處理,而是儘可能快將資料轉由應用處理。
- 在處理 telnet 或 rlogin 等互動模式的連線時,該標誌總是置位的。
-
RST:復位標誌。復位標誌有效,重建連線。
-
SYN:同步標誌。同步序列編號(Synchronize Sequence Numbers)欄有效。
- 在連線建立時用來同步序號。當 SYN=1,ACK=0 時表示這是一個連線請求報文段。若對方同意建立連線,則響應報文中 SYN=1,ACK=1。
-
FIN:結束標誌。用來釋放一個連線,當 FIN=1 時,表示此報文段的傳送方的資料已傳送完畢,並要求釋放運輸連線。
-
視窗:視窗值作為接收方讓傳送方設定其傳送視窗的依據。流量控制。
-
校驗和:檢驗和覆蓋了整個的TCP報文段:TCP首部和TCP資料。這是一個強制性的欄位,一定是由發端計算和儲存,並由收端進行驗證。
-
緊急指標:緊急指標是一個正的偏移量,和序號欄位中的值相加表示緊急資料最後一個位元組的序號。
- TCP的緊急方式是傳送端向另一端傳送緊急資料的一種方式。
- 只有當 URG 標誌置1時緊急指標才有效。
-
選項:長度可變,最長可達40位元組。當沒有使用“選項”時,TCP的首部長度是20位元組。最後的填充欄位僅僅是為了使整個TCP首部長度是4位元組的整數倍。
18. TCP連線的建立與終止
18.1 引言
18.2 連線的建立與終止
18.2.1 建立連線
三次握手建立連線
-
參考圖:
-
建立連線的過程是由客戶端發起,服務端等待客戶請求
-
第一步:客戶端向伺服器端傳送一個SYN報文段(只有首部,且SYN被置 1),初始序號(ISN)隨機選擇,假設為num_a,ACK 置 0。(客戶端進入SYN_SEND狀態)
-
第二步:伺服器端收到 SYN報文段,便知道客戶端需要請求握手,從 SYN報文段 中提取對應的資訊,為該 TCP 連線分配 TCP 快取和變數,並向該客戶 TCP 傳送允許連線的報文段(握手應答報文)。這個報文段只有首部,包含3個重要的資訊:(建立客戶端-->服務端的連線)(伺服器進入SYN_RECV狀態)
- SYN與ACK標誌位1
- 將TCP報文段首部的確認序號欄位設定為 num_a+1(這個num_a(ISN)是從握手請求報文中得到)。
- 伺服器隨機選擇自己的初始序號(ISN,注意此ISN是伺服器端的ISN,假設為num_b),並將其放置到TCP報文段首部的序號欄位中。
-
第三步:客戶端接收到伺服器端的握手應答後,會將 SYN 置 0,ACK 置 1,確認序號置為 num_b+1, 設定視窗值,可以新增資料域的報文段發給伺服器端。同時給該TCP連線分配快取和變數。(建立服務端-->客戶端的連線)(客戶端和伺服器端都進入ESTABLISHED狀態)
18.2.2 終止連線
四次揮手終止連線
-
參考圖
-
第一步:主機A發出 FIN報文段(首部FIN被置 1),序號假設為 num_c,ACK 被置 1,但是確認序號是無效的。(主機A進入FIN_WAIT_1狀態)(主機B進入CLOSE_WAIT狀態)
-
第二步:當主機B收到 FIN報文段,它返回一個 ACK報文段(終止連線應答),確認序號為 num_c+1。此時斷開客戶端-->伺服器端的方向。(主機A進入FIN_WAIT_2狀態)
-
第三步:主機B發出 FIN報文段 向主機A請求終止連線,此時序號為 num_d,ACK 被置 1,但是確認序號是無效的。(主機B進入LAST_ACK狀態)
-
第四步:當主機A收到FIN報文段,它返回一個ACK報文段(終止連線應答),確認序號為 num_d+1。此時斷開伺服器端-->客戶端的連線。(主機A進入TIME_WAIT狀態)(主機B進入CLOSE狀態)
18.2.3 TCP 連線狀態變遷圖
檢視:
18.3 連線建立超時
18.4 最大報文段長度
最大報文段長度(MSS)表示TCP傳往另一端的最大塊資料的長度。當一個連線建立時,連線的雙方都要通告各自的 MSS。
我們已經見過MSS都是1024。這導致 IP 資料包通常是4 0位元組長:2 0位元組的TCP首部和2 0位元組的 IP 首部。
使用IEEE 802.3的封裝,它的MSS可達 1452 位元組。
18.5 TCP 半關閉
TCP 是全雙工的。主要斷開一線的連線,剩下一線還可以正常工作。如A只能發,B只能收。
為了使用這個特性,程式設計介面必須為應用程式提供一種方式來說明:
- 我已經完成了資料傳送,因此傳送一個檔案結束(FIN)給另一端,但我還想接收另一端發來的資料,直到它給我發來檔案結束(FIN)。
18.6 TCP 狀態變遷圖
檢視:
18.6.1 2MSL 等待狀態
MSL:報文段最大生存時間,它是任何報文段被丟棄前在網路內的最長時間。
等待 2MSL 原因:
- 保證 TCP 協議的全雙工連線能夠可靠關閉。
- 如果 B 端沒有收到 ACK ,觸發超時重發 FIN報文段,A 端依然能處理重發 ACK。如果 A 端直接 CLOSE 狀態,就不能保證 B 端收到 ACK。
- 保證這次連線的重複資料段從網路中消失。
- 保證下次連線收到的資料包文段都是來自新連線的目標端。
18.6.2 平靜時間的概念
平靜時間(quiet time):TCP在重啟動後的 MSL 秒內不能建立任何連線。
- 防止重啟前的報文段被認為新的報文。所以要保證重啟前的報文在網路中消失才能重啟。
18.6.3 FIN_WAIT_2 狀態
在揮手斷開連線過程中,如果 B端(第三次揮手)不發出 FIN報文段 將會導致 A端 一直處於 FIN_WAIT_2 狀態。而 B端 一直處於 CLOSE_WAIT 狀態。
直至應用層決定關閉進行關閉才能跳出該狀態。
可以設立定時器維護這個狀態,但是必須在程式碼中註明此做法是違背協議的規範。
18.7 復位報文段
TCP首部中的 RST位元 是用於“復位”的。
18.7.1 到不存在的埠的連線請求
產生復位的一種常見情況是當連線請求到達時,目的埠沒有程式正在聽。
- 對於 UDP,當一個資料包到達目的埠時,該埠沒在使用,它將產生一個ICMP埠不可達的資訊。
- 而TCP則使用復位。
18.7.2 異常終止一個連線
正常的終止連線請求是傳送 FIN報文段。
但是直接傳送 RST復位報文段 也可以終止連線。稱為 異常終止。
異常終止一個連線對應用程式來說有兩個優點:
- 丟棄任何待發資料並立即傳送復位報文段;
- RST的接收方會區分另一端執行的是異常關閉還是正常關閉。
18.7.3 檢測半邊開啟連線
半開啟(Half-Open):一方已經關閉或異常終止連線而另一方卻還不知道的TCP連線。
當異常的一方回覆正常且繼續收到來自另一方的資料(另一方並不知道這邊的情況),這時接收方無法辨認接受的資料是什麼,於是傳送 RST復位報文段 作為應答。
tip:另一種檢測方法:
- 可以採用 keepalive 功能。(在23章-TCP的保活定時器 中說明)
18.8 同時開啟
同時開啟(simultaneous open):每一方必須傳送一個SYN,且這些SYN必須傳遞給對方。
TCP是特意設計為了可以處理同時開啟,對於同時開啟它僅建立一條連線而不是兩條連線。
(其他的協議族,最突出的是OSI運輸層,在這種情況下將建立兩條連線而不是一條連線)
- 兩端幾乎在同時傳送 SYN,並進入SYN_SENT狀態。當每一端收到SYN時,狀態變為SYN_RCVD,同時它們都再發SYN並對收到的SYN進行確認。當雙方都收到SYN及相應的ACK時,狀態都變遷為ESTABLISHED。
18.9 同時關閉
18.10 TCP 選項
當前 TCP 選項格式:
-
每個選項的開始是1位元組k i n d欄位,說明選項的型別。
18.11 TCP 伺服器的設計
大多數的TCP伺服器程式是併發的。當一個新的連線請求到達伺服器時,一般會用一個新的程式或執行緒處理新客戶請求。(不同的作業系統有不同的處理方案)
TCP伺服器忙時處理連線請求規則:
- 服務端維護一個固定長度的連線佇列。該佇列維護著已經三次握手完成,但是還沒有被應用層接受的連線。
- 注意: 區分TCP接受一個連線是將其放入這個佇列,而應用層接受連線是將其從該佇列中移出。
- 應用層將指明該佇列的最大長度,這個值通常稱為積壓值 ( backlog)。
- 當一個連線請求(即SYN)到達時,TCP使用一個演算法,根據當前連線佇列中的連線數來確定是否接收這個連線。
- 如果對於新的連線請求,該 TCP 監聽的端點的連線佇列中還有空間,TCP 模組將對SYN進行確認並完成連線的建立。但應用層只有在三次握手中的第三個報文段收到後才會知道這個新連線時。另外,當客戶程式的主動開啟成功但伺服器的應用層還不知道這個新的連線時,它可能會認為伺服器程式已經準備好接收資料了(如果發生這種情況,伺服器的TCP僅將接收的資料放入緩衝佇列)。
- 如果對於新的連線請求,連線佇列中已沒有空間,TCP將不理會收到的SYN。也不發回任何報文段(即不發回RST)。如果應用層不能及時接受已被 TCP 接受的連線,這些連線可能佔滿整個連線佇列,客戶的主動開啟最終將超時。
19. TCP 的互動資料流
19.1 引言
建立在TCP協議上的應用層協議有非常多,如FTP、HTTP、Telnet、Rlogin等。這些協議依據資料傳輸的多少能夠分為兩類:
- 互動資料型別:如 Telnet,這類協議一般僅僅做小流量的資料交換,比方每按下一個鍵,要回顯一些字元。
- 成塊資料型別:如 FTP,這類協議須要傳輸的資料比較多,一般傳輸的資料量比較大。
19.2 互動式輸入
檢視:
一個回顯鍵入會產生4個報文段:(也可以把②和③合併)
- 客戶發出的按鍵資料包文段。
- 伺服器發出的按鍵確認報文段。
- 伺服器發出的回顯資料包文段。
- 客戶發出的回顯確認報文段。
19.3 經受時延的確認
通常 TCP 在接收到資料時並不立即傳送 ACK;它推遲傳送,以便將 ACK 與需要沿該方向傳送的資料一起傳送(有時稱這種現象為資料捎帶 ACK)。
絕大多數實現採用的時延為200 ms,也就是說,TCP 將以最大 200 ms的時延等待是否有資料一起傳送。
19.4Nagle 演算法
在上面描述的 Rlogin 回顯中,一個字元就回顯一次,其中產生的分組為 41位元組的分組:20位元組IP首部 + 20位元組TCP首部 + 1位元組的資料。
若把這些小分組放到廣域網上,會增加擁塞。
Nagle 演算法就是解決上面的情況。
Nagle:
- 該演算法要求一個 TCP 連線上最多隻能有一個未被確認的未完成的小分組。在該分組的確認到達之前不能傳送其他的小分組。
- TCP 收集這些少量的分組,並在確認到來時以一個分組的方式發出去。
- Nagle 優點是自適應:確認到達得越快,資料也就傳送得越快。
19.4.1 關閉 Nagle 演算法
插口API使用者可以使用 TCP_NODELAY 選項來關閉Nagle演算法。
19.5 視窗大小通告
20. TCP的成塊資料流
20.1 引言
主要涉及:
- 滑動視窗-流量控制
- 慢啟動
20.2 正常資料流
20.3 滑動視窗
利用滑動視窗實現流量控制。
檢視:
-
視窗兩個邊沿的相對運動增加或減少了視窗的大小。
- 稱視窗左邊沿向右邊沿靠近為視窗合攏。這種現象發生在資料被髮送和確認時。
- 當視窗右邊沿向右移動時將允許傳送更多的資料,我們稱之為視窗張開。這種現象發生在另一端的接收程式讀取已經確認的資料並釋放了 TCP 的接收快取時。
- 當右邊沿向左移動時,我們稱之為視窗收縮。
-
注意,視窗只是說明接收方當前能接收的最大容量而已,傳送方不必一次性發滿整個視窗大小。
20.4 視窗大小
由接收方提供的視窗的大小通常可以由接收程式控制,這將影響 TCP 的效能。
插口API允許程式設定傳送和接收快取的大小。
接收快取的大小是該連線上所能夠通告的最大視窗大小。
應用程式可以通過修改插口快取大小來增加效能。
20.5 PUSH 標誌
傳送方使用 PUSH標誌 通知接收方將所收到的資料全部提交給接收程式。
這裡的資料包括與 PUSH 一起傳送的資料以及接收方 TCP 已經為接收程式收到的其他資料。
20.6 慢啟動
慢開始演算法處理屬於 TCP擁塞控制。
當主機開始傳送資料時,如果立即所大量資料位元組注入到網路,那麼就有可能引起網路擁塞,因為現在並不清楚網路的負荷情況。所以,較好的方法是 先探測一下,即由小到大逐漸增大傳送視窗。
慢開始和擁塞避免:
- 傳送方維持一個擁塞視窗 cwnd ( congestion window )的狀態變數。
- 擁塞視窗的大小取決於網路的擁塞程度,並且動態地在變化。
- 傳送方讓自己的傳送視窗等於擁塞視窗。
- 傳送方控制擁塞視窗的原則是:
- 只要網路沒有出現擁塞,擁塞視窗就再增大一些,以便把更多的分組傳送出去。
- 但只要網路出現擁塞,擁塞視窗就減小一些,以減少注入到網路中的分組數。
慢開始演算法:
- 通常在剛剛開始傳送報文段時,先把擁塞視窗 cwnd 設定為一個最大報文段MSS的數值。
- 在每收到一個對新的報文段的確認後,把擁塞視窗增加至多一個MSS的數值。
- 用這樣的方法逐步增大傳送方的擁塞視窗 cwnd ,可以使分組注入到網路的速率更加合理。
- 為了防止擁塞視窗cwnd增長過大引起網路擁塞,還需要設定一個慢開始門限 ssthresh 狀態變數。
- 當 cwnd < ssthresh 時,使用上述的慢開始演算法。
- 當 cwnd = ssthresh 時,既可使用慢開始演算法,也可使用擁塞控制避免演算法。
- 當 cwnd > ssthresh 時,停止使用慢開始演算法而改用擁塞避免演算法。
擁塞避免:
- 讓擁塞視窗cwnd緩慢地增大,即每經過一個往返時間RTT就把傳送方的擁塞視窗cwnd加1,而不是加倍。
注意:
- 符合以下條件之一即可使用 擁塞避免 ,不一定要達到 ssthresh 值。
- 當 cwnd > ssthresh 時。
- 傳送方判斷網路出現擁塞(其根據就是沒有收到確認)。
20.7 成塊資料的吞吐量
20.7.1 頻寬時延乘積
頻寬時延乘積:capacity(bit) = bandwidth (b/s) × round-trip time ( s )。
- 這個值依賴於 網路速度 和 兩端的 RTT。
20.7.2 擁塞
部分擁塞情景:
- 當資料到達一個大的管道(如一個快速區域網)並向一個較小的管道(如一個較慢的廣域網)傳送時便會發生擁塞。
- 當多個輸入流到達一個路由器,而路由器的輸出流小於這些輸入流的總和時也會發生擁塞。
20.8 緊急方式
TCP提供了 緊急方式 ( u rgent mode),它使一端可以告訴另一端有些具有某種方式的 緊急資料 已經放置在普通的資料流中。
另一端被通知這個緊急資料已被放置在普通資料流中,由接收方決定如何處理。
URG位元 被置 1,並且一個 16bit 的緊急指標 被置為一個正的偏移量,該偏移量必須與 TCP 首部中的序號欄位相加,以便得出緊急資料的最後一個位元組的序號。
緊急方式的作用:
- 兩個最常見的例子是 Telnet 和 Rlogin。當互動使用者鍵入中斷鍵時。(參考 卷一26 章)
- 另一個例子是 FTP,當互動使用者放棄一個檔案的傳輸時。(參考 卷一27 章)
21. TCP的超時與重傳
21.1 引言
對每個連線,TCP管理4個不同的定時器:
- 重傳定時器使用於當希望收到另一端的確認。功能如擁塞避免。
- 堅持( persist )定時器使視窗大小資訊保持不斷流動,即使另一端關閉了其接收視窗。
- 保活( keepalive )定時器可檢測到一個空閒連線的另一端何時崩潰或重啟。
- 2MSL定時器測量一個連線處於TIME_WAIT狀態的時間。
21.2 超時與重傳的簡單例子
21.3 往返時間測量
TCP 超時與重傳中最重要的部分就是對一個給定連線的往返時間(RTT)的測量。
由於路由器和網路流量均會變化,因此我們認為這個時間可能經常會發生變化,TCP 應該跟蹤這些變化並相應地改變其超時時間。
首先 TCP 必須測量在傳送一個帶有特別序號的位元組和接收到包含該位元組的確認之間的 RTT。
用 M 表示所測量到的 RTT。
計算:new_RTTS = (1-α)x(old_RTTS) + αx(new_RTT)
- α:值推薦為 1/8 的平滑因子。
- new_RTTS:新的平滑往返時間。
- old_RTTS:舊的平滑往返時間。
- new_RTT:測量到的 RTT。
- 每個新估計的90%來自前一個估計,而10%則取自新的測量。
計算:RTO = RTTS+4x(RTTD)
- RTO:重傳超時時間。
- RTTD:是RTT的偏差加權的平均值,與 RTTS 和新得到的 RTT 樣本之差有關。
- 第一次測量到 RTT 樣本時,RTTS 的取值就為測量的 RTT 樣本值。
計算:new_RTTD = (1-β)x(lod_RTTD) + βx(new_RTT)
- β:推薦值為 1/4。
- 第一次測量 RTTD 時,RTTD 的取值為 RTT 樣本值的一半。
如上所述就是測量 RTO 公式和引數。
但是仍然有一個問題需要確定,在計算加權平均 RTTS 時,只要資料包重傳沒有使用往返時間,就可以得出 RTTS 和 RTO 是準確的。
當之後出現超重時,新 RTO 是 old_RTO 的2倍。
21.4 往返時間RTT的例子
21.5 擁塞舉例
21.6 擁塞避免演算法
擁塞避免演算法是一種處理丟失分組的方法。
擁塞避免演算法和慢啟動演算法需要對每個連線維持兩個變數:一個擁塞視窗 cwnd 和一個慢 啟動門限 ssthresh。
符合以下條件之一即可使用 擁塞避免 ,不一定要達到 ssthresh 值。
- 當 cwnd > ssthresh 時。
- 傳送方判斷網路出現擁塞(其根據就是沒有收到確認)。
21.7 快速重傳與快速恢復演算法
快重傳:
快重傳演算法:
- 首先要求接收方每收到一個失序的報文段後就立即發出重複確認(為的是使傳送方及早知道有報文段沒有到達對方)而不要等到自己傳送資料時才進行捎帶確認。
- 快重傳演算法還規定,傳送方只要一連收到三個重複確認就應當立即重傳對方尚未收到的報文段,而不必繼續等待M3設定的重傳計時器到期。
快恢復:
快恢復有兩個要點:
-
當傳送方連續收到三個重複確認,就執行乘法減小演算法,把慢開始門限 ssthresh 減半。
-
把 cwnd 值設定為 慢開始門限 ssthresh 減半後的數值,然後開始執行擁塞避免演算法(加法增大),使擁塞視窗緩慢地線性增大。(而不是重新執行慢開始演算法)
21.8 擁塞舉例(續)
21.9 按每條路由進行度量
如今較新的TCP實現的路由表項中維持了很多指標,當一個TCP連線關閉時,假設已經發出了足夠多的資料來獲得有意義的統計資料。
且目的節點的路由表項不是一個預設表項。
那麼下列資訊就儲存在在路由表項中以備下次使用:
- 被平滑的RTT
- 被平滑的均值偏差
- 慢啟動門限
足夠多的資料:是指 16 個視窗的資料。
21.10 ICMP的差錯
TCP 能夠遇到的最常見的 ICMP 差錯就是:
- 源站抑制
- 主機不可達
- 網路不可達
處理:
- 一個接收到的源站抑制引起擁塞視窗 cwnd 被置為1個報文段大小來發起慢啟動,但是慢 啟動門限 ssthresh 沒有變化,所以視窗將開啟直至它或者開放了所有的通路(受視窗大小和往返時間的限制)或者發生了擁塞。
- 一個接收到的主機不可達或網路不可達實際上都被忽略,因為這兩個差錯都被認為是短暫現象。這有可能是由於中間路由器被關閉而導致選路協議要花數分鐘才能穩定到另一個替換路由。
- 在這個過程中就可能發生這兩個ICMP差錯中的一個,但是連線並不必被關閉。相反,TCP試圖傳送引起該差錯的資料,儘管最終有可能會超時。
21.11 重新分組
當TCP超時並重傳時,它不一定要重傳同樣的報文段。
相反,TCP允許進行重新分組而傳送一個較大的報文段,這將有助於提高效能( 接收方宣告的MSS)。
在協議中這是允許的,因為TCP是使用位元組序號而不是報文段序號來進行識別它所要傳送的資料和確認。
TCP資料長度:
**TCP資料長度 = IP包總長度(IP首部裡)- IP首部長度(IP首部裡)- TCP首部長度 **
22. TCP 的堅持定時器
22.1 引言
如果接收方傳送一個大小為 0 的視窗到傳送方,那傳送方就停止傳送資料,直至視窗為非 0。
TCP 不對 ACK 報文段進行確認,只確認那些包含有資料的 ACK 報文段。
22.2 例子&解決
bug情景:如果一個確認丟失了,則雙方就有可能因為等待對方而使連線終止:
- 接受方等待接收資料
- (因為它已經向傳送方通告了一個非0的視窗,但是丟失了,且接收方是不對這個報文段進行確認的)
- 而傳送方在等待允許它繼續傳送資料的視窗更新。
解決:為防止這種死鎖情況發生,傳送方使用了一個堅持定時器(persist timer)來週期性地向接收方查詢,以便發現視窗是否已增大,範圍在5~60 s之間。這些從傳送方發出的報文段稱為視窗探查(window probe)。
22.3 糊塗視窗綜合症
糊塗視窗綜合症SWS(Silly Window Syndrome):
- 發生在兩端中的任何一端。
- 接收方可以通告一個小的視窗(而不是一直等到有大的視窗時才通告)。
- 而傳送方也可以傳送少量的資料(而不是等待其他的資料以便傳送一個大的報文段)。
可以在任何一端採取措施避免出現糊塗視窗綜合症的現象:
- 接收方不通告小視窗。通常的演算法是接收方不通告一個比當前視窗大的視窗(可以為0),除非視窗可以增加一個報文段大小(也就是將要接收的MSS)或可以增加接收方快取空間的一半,不論實際有多少。
- 傳送方避免出現糊塗視窗綜合症的措施是隻有以下條件之一滿足時才傳送資料:
- 可以傳送一個滿長度的報文段。
- 可以傳送至少是接收方通告視窗大小一半的報文段。
- 可以傳送任何資料並且不希望接收ACK(也就是沒有還未被確認的資料)或該連線上不能使用 Nagle 演算法。
- 該條件使在有尚未被確認的資料(正在等待被確認)以及在不能使用Nagle演算法的情況下,避免傳送小的報文段。
- 例如如果應用程式在程式小資料的寫操作(例如比該報文段還小),該條件就可以避免出現糊塗視窗綜合症。
23. TCP 的保活定時器
情景:啟動一個客戶與伺服器建立一個連線,然後離去數小時、數天、數個星期或數月,而連線依然保持。中間路由器可以崩潰和重啟,電話線可以被結束通話再連通,但是隻有兩端的主機沒有被重啟,則連線依然保持建立。這種非活動狀態可以導致應用程式中的任何一個終止其活動。
許多時候一個伺服器希望知道客戶主機是否崩潰並關機或者崩潰又重新啟動。許多實現提供的保活定時器可以提供這種能力。
保活並不是 TCP 規範中的一部分。Host Requirements RFC提供了3個不使用保活定時器的理由:
- 在出現短暫差錯的情況下,這可能會使一個非常好的連線釋放掉;
- 它們耗費不必要的頻寬;
- 在按分組計費的情況下會在網際網路上花掉更多的錢。
如果一個給定的連線在2個小時之內沒有任何動作,則伺服器向客戶傳送一個探查報文段。客戶主機必須處於以下4個狀態之一:
- 客戶主機仍然正常執行,並從伺服器可達。客戶的TCP響應正常,而伺服器也知道對方是正常工作的。伺服器在2個小時以後將保活定時器復位。如果在2個小時定時器到時間之前應用程式的通訊量通過此連線,則定時器在交換資料後的未來2個小時再復位。
- 客戶主機已經崩潰,並且關閉或者正在重新啟動。在任何一種情況下,客戶的TCP都沒有響應。伺服器將不能夠收到探查的響應,並在75s後超時。伺服器總共傳送10個這樣的探查,每個間隔75秒。如果伺服器沒有收到一個響應,它就認為客戶主機已經關閉並終止連線。
- 客戶主機崩潰並已經重新啟動。這時伺服器將收到一個對其保活探查的響應,但是這個響應是一個復位,使得伺服器終止這個連線。
- 客戶主機正常執行,但是從伺服器不可達。這與狀態2相同,因為TCP不能夠區分狀態4與狀態2之間的區別,它所能發現的就是沒有接收到探查的響應。
在這2、3、4情況下,伺服器application將收到來自它的TCP的差錯報告(通常伺服器已經向網路發出了讀操作請求,然後等待來自客戶的資料。如果保活功能返回一個差錯,則該差錯將作為讀操作的返回值給伺服器)。其差錯對應如下:
客戶主機已經崩潰,並且關閉或者正在重新啟動 | 類似 連線超時 |
客戶主機崩潰並已經重新啟動 | 類似 連線被對方復位 |
客戶主機正常執行,但是從伺服器不可達 | 類似 連線超時 |
24. TCP 的未來和效能
有興趣可看原文
參考
連結: