日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

TCP 可靠传输机制详解

發(fā)布時間:2024/2/28 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 TCP 可靠传输机制详解 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

目錄

TCP協(xié)議的特點

TCP 報文段

TCP "三次握手"

TCP "四次揮手"

客戶端和服務器端所經(jīng)歷的狀態(tài)

TCP 可靠傳輸

TCP流量控制

TCP擁塞控制

面試相關問題


前言

本篇博文主要是為了復習 TCP 協(xié)議而做的總結。其中很多內容都是來自于《計算機網(wǎng)絡》,《Linux網(wǎng)絡編程》,《TCP/IP詳解》等書籍。首先可以從 TCP 協(xié)議思維導圖看到本文的大致內容。雖然有很多內容,但 "三次握手""四次揮手"可靠傳輸機制?才是本文的重點,其中會用 wireshark 抓取相應的包用于協(xié)議分析。最后也會總結一些面試常問的問題。TCP 協(xié)議思維導圖如下:

?

?

TCP協(xié)議的特點

TCP 是在不可靠的網(wǎng)絡層之上實現(xiàn)的可靠的數(shù)據(jù)傳輸協(xié)議,它主要解決傳輸?shù)目煽俊⒂行颉o丟失和不重復的問題。TCP 的主要特點有:

(1) TCP 是面向連接的傳輸層協(xié)議。

(2) 每一條 TCP 連接只能有兩個端點,每一條 TCP 連接只能是點對點的(一對一)。

(3) TCP 提供可靠的交付服務,保證傳送的數(shù)據(jù)無差錯、不丟失、不重復且有序。

(4) TCP 提供全雙工通信,TCP 允許通信雙方的應用進程在任何時候都能發(fā)送數(shù)據(jù),為此 TCP 連接的兩端都設有發(fā)送緩存和接收緩存,用來臨時存放雙向通信的數(shù)據(jù)。

發(fā)送緩存用來暫存以下數(shù)據(jù):①應用程序傳送給發(fā)送方 TCP 準備發(fā)送的數(shù)據(jù);② TCP 已發(fā)送出但尚未收到的確認的數(shù)據(jù)。

接收緩存用來暫存以下數(shù)據(jù):①按序到達的但尚未被接收應用程序讀取的數(shù)據(jù);②不按序到達的數(shù)據(jù)。

(5) TCP 是面向字節(jié)流的,雖然應用程序和 TCP 的交互是一次一個數(shù)據(jù)塊,但 TCP 把應用程序交下來的數(shù)據(jù)看成僅僅是一連串的無結構的字節(jié)流。

?

TCP 報文段

TCP 傳輸?shù)臄?shù)據(jù)單元稱為報文段。一個 TCP 報文段分為 TCP 首部和 TCP 數(shù)據(jù)兩部分,整個 TCP 段作為 IP 數(shù)據(jù)報的數(shù)據(jù)部分封裝在 IP 數(shù)據(jù)報中。

各字段的含義如下:

(1) 源端口和目的端口:各占 2 個字節(jié)。端口是運輸層與應用層的服務接口。運輸層的復用和分用功能都要通過端口才能實現(xiàn)。

(2) 序號:占 4 個字節(jié)。TCP是面向字節(jié)流的,所以 TCP 連接中傳送的數(shù)據(jù)流中的每一個字節(jié)都編上一個序號。序號字段的值則指的是本報文段所發(fā)送的數(shù)據(jù)的第一個字節(jié)的序號。例如,一個報文段的序號字段值是 200,攜帶的數(shù)據(jù)總共有 100 字節(jié),表明這個報文段的數(shù)據(jù)的最后一個字節(jié)的序號是 299,所以下一個報文段的數(shù)據(jù)序號應從 300 開始。

(3) 確認號:占 4 個字節(jié),是期望收到對方的下一個報文段的數(shù)據(jù)的第一個字節(jié)的序號。若確認號為 N,表明前 N-1 的所有數(shù)據(jù)都已經(jīng)正確接收。例如,B 正確的收到了 A 發(fā)送過來的一個報文段,其序號字段是指 501,而數(shù)據(jù)長度是 200 字節(jié)(序號 501~700),表明 B 正確的收到了 A 發(fā)送的序號 700 之前的數(shù)據(jù)。因此 B 希望收到 A 的下一個數(shù)據(jù)序號是 701,所以 B 在發(fā)送給 A 的確認報文段中應把確認號設置成 701。

(4) 數(shù)據(jù)偏移量:占 4 位,這里不是 IP 數(shù)據(jù)報分片的那個數(shù)據(jù)偏移,而是表示首部長度,它指出 TCP 報文段的數(shù)據(jù)起始處距離 TCP 報文段的起始處有多遠。該字段若為 15,則表明 TCP 首部達到最大長度 60 字節(jié)(以 4 字節(jié)為計算單位乘以 15)。

(5) 保留:占 6 位,目前不使用,所以置為 0。

(6) 緊急位 URG:當其值為 1 時,表明緊急指針字段有效。它會告訴系統(tǒng)此報文段中有緊急數(shù)據(jù),應該盡快傳送。但是 URG 需要和緊急指針配套使用,即從第一個字節(jié)到緊急指針所指字節(jié)就是緊急數(shù)據(jù)。

(7)?確認位 ACK:只有當 ACK = 1 時,確認號字段才有效。TCP 規(guī)定,在連接建立后所有傳送的報文段都必須把 ACK 置為 1。

(8) 推送位 PSH:TCP 收到 PSH = 1 的報文段,就盡快的交付接收應用進程,而不再等到整個緩存都填滿了后再向上交付。

(9) 復位位 RST:當 RST = 1 時,表明 TCP 連接中出現(xiàn)了嚴重的錯誤,必須釋放連接,然后再重新建立運輸連接。

(10)?同步位 SYNSYN = 1表示這是一個連接請求或連接接收報文。當 SYN = 1,ACK = 0 時,表明這是一個連接請求報文,對方若同意建立連接,則在響應報文中使用 SYN = 1,ACK = 1。

(11)?終止位 FIN:用來釋放一個連接。FIN = 1 表明此報文段的發(fā)送方的數(shù)據(jù)已經(jīng)發(fā)送完畢,并要求釋放傳輸連接。

(12) 窗口:占 2 個字節(jié)。它指出現(xiàn)在允許對方發(fā)送的數(shù)據(jù)量,接收方的數(shù)據(jù)緩存空間是有限的,所以使用窗口值作為接收方讓發(fā)送方設置其發(fā)送窗口的依據(jù),單位是字節(jié)。例如,設確認號是 101,窗口字段是 1000。這就表明,從 101 號開始,發(fā)送此報文段的一方還有接收 1000 字節(jié)數(shù)據(jù)(字節(jié)序號是 101~1100)的接收緩存空間。

(13) 校驗和:占 2 個字節(jié)。它的檢驗范圍包括首部和數(shù)據(jù)部分。計算時要在 TCP 報文段前面加上 12 字節(jié)的偽首部。

(14) 緊急指針:占 16 位,指出在本報文段中緊急數(shù)據(jù)共有多少個字節(jié)。

(15) 選項:長度可變。TCP 最初只規(guī)定了一種選項,即最大報文段長度(MSS)。

(16) 填充:這是為了使整個首部長度是 4 字節(jié)的整數(shù)倍。

?

主要需要理解以下幾個字段:

源端口號和目標端口號:誰發(fā)的和發(fā)給誰(你是誰?從哪里來?到哪里去?)

序號:為了解決亂序問題;

確認序號:發(fā)出去的包應該有確認,沒有收到就應該重新發(fā)送,直到送達;

狀態(tài)位:常見的有 SYN、ACK、FIN,分別表示發(fā)起一個連接、確認和結束連接;

窗口大小:用于 TCP 流量控制,通信雙方各聲明一個窗口,說明自己當前的處理能力發(fā)送的太快,處理不了,發(fā)的太慢,影響發(fā)送的效率

?

TCP "三次握手"

第一次握手:客戶端首先向服務器發(fā)送一個連接請求報文段。這個特殊的報文段中不含應用層數(shù)據(jù),其首部中的 SYN 標志位被置為 1。另外,客戶端會隨機選擇一個起始號 seq = x (連接請求報文不攜帶數(shù)據(jù),但要消耗掉一個序號)客戶端進程進入 SYN_SENT(同步發(fā)送)狀態(tài)。?

第二次握手:服務器收到連接請求報文段后,如同意建立連接,就向客戶端發(fā)回確認,并為該 TCP 連接分配 TCP 緩存和變量。在確認報文段中,SYN 和 ACK 位都被置為 1,確認號字段的值為 x+1,并且服務器端隨機產生起始序號 seq = y(確認報文不攜帶數(shù)據(jù),但也要消耗掉一個序號)。確認報文段同樣不包含應用層數(shù)據(jù)。服務端進入SYN_RCVD(同步接收)狀態(tài)。

第三次握手:當客戶端收到確認報文段后,還要向服務器給出確認,并且也要給該連接分配緩存和變量。這個報文段的 ACK 標志位被置為 1,序號字段為 x+1,確認號字段為 ack = y+1。該報文可以攜帶數(shù)據(jù),如果不攜帶數(shù)據(jù)則不消耗序號。客戶端進入 ESTABLISHED(建立連接)狀態(tài)。

在成功的完成以上三步后,TCP 連接就建立了,接下來就可以傳送應用層數(shù)據(jù)了。TCP 提供的是全雙工通信,因此通信雙方的應用進程在任何時候都能發(fā)送數(shù)據(jù)。注意:服務器端的資源是在完成第二次握手時開始分配的,所以服務器易于受到 SYN 洪泛攻擊。

?

TCP "三次握手" 報文詳細分析

TCP報文:[29 05 01 bb f4 0a 2c 77 1f a9 79 d2 50 10 01 00 b9 84 00 00] 為了更易理解:[29 05 01 bb][f4 0a 2c 77][1f a9 79 d2][50 10 01 00][b9 84 00 00] 源端口:2905(HEX) = 10501(DEC) 目的端口:01bb(HEX) = 443(DEC) 序號:1 為什么連接過程中的"序號"顯示1,而相應字段的值不是1。詳情請看面試問題的(6) 確認號:1 十六進制[50 10]轉化成二進制[0101 0000 0001 0000] 數(shù)據(jù)偏移(占4位):單位是4個字節(jié),此字段值為5,所以TCP首部的長度是20字節(jié)。 保留(占6位):保留為今后使用,目前置為0。 緊急位URG:0 確認位ACK:1 推送位PSH:0 復位位RST:0 同步位SYN:0 終止位FIN:0 窗口:0100(HEX) = 256(DEC) 檢驗和:0xb984 緊急指針:0

?

TCP "四次揮手"

參與 TCP 連接的兩個進程中的任何一個都能終止該連接。

第一步:若客戶端打算關閉連接,就向其服務器發(fā)送一個連接釋放報文段,并停止再發(fā)送數(shù)據(jù),主動關閉 TCP 連接,該報文段的 FIN 標志位被置為 1,seq = u,它等于前面已傳送過的數(shù)據(jù)的最后一個字節(jié)的序號加 1(FIN 報文段即使不攜帶數(shù)據(jù),也要消耗掉一個序號)。TCP 是全雙工的,即可以想象成是一條 TCP 連接上有兩條數(shù)據(jù)通路。當發(fā)送 FIN 報文時,發(fā)送 FIN 的一端就不能再發(fā)送數(shù)據(jù),也就是關閉了其中一條數(shù)據(jù)通路,但另一條沒有關閉的數(shù)據(jù)通路仍可以發(fā)送數(shù)據(jù)。

第二步:服務器收到連接釋放報文段后就會發(fā)出確認,確認號為 ack = u+1,而這個報文段自己的序號是 v,等于它前面已傳送過的數(shù)據(jù)的最后一個字節(jié)的序號加 1。此時,從客戶端到服務器這個方向的連接就釋放了,TCP 連接處于半關閉狀態(tài)。但服務器若發(fā)送數(shù)據(jù),客戶端仍能接收,即從服務器到客戶端這個方向的連接并未關閉。

第三步:若服務器已經(jīng)沒有要向客戶端發(fā)送的數(shù)據(jù),就釋放連接,此時發(fā)出 FIN = 1 的連接釋放報文段。

第四步:客戶端收到連接釋放報文段后,必須發(fā)出確認。在確認報文段中,ACK 字段被置為 1,確認號 ack = w+1,序號 seq = u+1。此時 TCP 連接還沒有釋放掉,必須經(jīng)過 2MSL 后,客戶端才進入連接關閉狀態(tài)。

?

客戶端和服務器端所經(jīng)歷的狀態(tài)

結合連接和釋放過程圖看狀態(tài)轉換圖,以下是客戶端的狀態(tài)轉換圖:

服務器端的狀態(tài)轉換圖:

完整的狀態(tài)轉換圖(紅線是客戶端,藍線是服務器,它們表示正常的連接和斷開的過程):

?

斷開過程中的狀態(tài)解析

(1)?FIN_WAIT_1:? FIN_WAIT_1 和 FIN_WAIT_2 狀態(tài)的真正含義都是表示等待對方的 FIN 報文。而這兩種狀態(tài)的區(qū)別是:? FIN_WAIT_1 狀態(tài)實際上是當 socket?在 ESTABLISHED 狀態(tài)時,它想主動關閉連接,向對方發(fā)送了 FIN 報文,此時該 socket??即進入到 FIN_WAIT_1 狀態(tài)。而當對方回應 ACK 報文后,則進入到 FIN_WAIT_2 狀態(tài)。當然在實際的正常情況下, FIN_WAIT_1 狀態(tài)一般是比較難見到的(這個過程比較快),而 FIN_WAIT_2 狀態(tài)還有時常常可以用 netstat 看到(因為有一方還在發(fā)數(shù)據(jù))。(主動關閉連接)

(2)?FIN_WAIT_2:上面已經(jīng)詳細解釋了這種狀態(tài),實際上 FIN_WAIT_2 狀態(tài)下的 socket 表示半連接,即有一方要求 close 連接,但另外一方告訴對方,我暫時還有點數(shù)據(jù)需要傳送給你(發(fā)送 ACK 報文),稍后再關閉連接。(主動關閉連接)

(3)?TIME_WAIT:表示收到了對方的 FIN 報文,并發(fā)送出了 ACK 報文,就等 2MSL 后即可回到 CLOSED 可用狀態(tài)了。如果 FIN_WAIT_1 狀態(tài)下,收到了對方同時帶 FIN 標志和 ACK 標志的報文時(被動方?jīng)]有數(shù)據(jù)發(fā)送),可以直接進入到 TIME_WAIT 狀態(tài),而無須經(jīng)過 FIN_WAIT_2 狀態(tài)。(主動關閉連接)

(4)?CLOSING(比較少見): 這種狀態(tài)比較特殊,實際情況中應該是很少見,屬于一種比較罕見的例外狀態(tài)。正常情況下,當你發(fā)送 FIN 報文后,按理來說是應該先收到(或同時收到)對方的 ACK 報文,再收到對方的 FIN 報文。但是 CLOSING 狀態(tài)表示你發(fā)送 FIN 報文后,并沒有收到對方的 ACK 報文,反而卻也收到了對方的 FIN 報文。什么情況下會出現(xiàn)此種情況呢?其實細想一下,也不難得出結論:那就是如果雙方幾乎在同時關閉一個 socket?的話,那么就出現(xiàn)了雙方同時發(fā)送 FIN 報文的情況,也即會出現(xiàn) CLOSING 狀態(tài),表示雙方都正在關閉 socket?連接。

(5)?CLOSE_WAIT: 被動方需要查看是否還有數(shù)據(jù)發(fā)送給對方,如果沒有的話,那么你也就可以關閉這個 socket,發(fā)送 FIN 報文給對方,即關閉連接。所以在 CLOSE_WAIT 狀態(tài)下,需要完成的事情是等待被動方去關閉連接。(被動方)

(6)?LAST_ACK: 它是被動關閉的一方在發(fā)送 FIN 報文后,最后等待對方的 ACK 報文。當收到 ACK 報文后,也即可以進入到 CLOSED 可用狀態(tài)了。(被動方)

(7)?CLOSED: 表示連接中斷。

?

小總結:

半關閉狀態(tài)的含義是:通信的一端可以發(fā)送 FIN 報文段給對方,告訴它本端已經(jīng)完成了數(shù)據(jù)的發(fā)送,但允許繼續(xù)接收來自對方的數(shù)據(jù),直到對方也發(fā)送 FIN 報文段以關閉連接。

當客戶端處于 FIN_WAIT_2 狀態(tài)時,服務器處于 CLOSE_WAIT 狀態(tài),這時可能會處于半連接狀態(tài),即服務器還有數(shù)據(jù)發(fā)給客戶端。如果服務器沒有數(shù)據(jù)發(fā)送,則發(fā)送 FIN 報文段關閉連接,而客戶端將確認并進入 TIME_WAIT 狀態(tài)。

上面的狀態(tài)轉換圖還給出了 FIN_WAIT_1 狀態(tài)直接進入 TIME_WAIT 狀態(tài)的一條線路,前提是處于 FIN_WAIT_1 的客戶端直接收到了帶有確認信息的 FIN 報文段(而不是正常情況下的先收到 ACK 報文段,再收到 FIN 報文段)。

?

斷開連接時的意外

當 TCP 連接發(fā)生一些物理上的意外情況時,例如網(wǎng)線斷開,linux 上的 TCP 實現(xiàn)會依然認為該連接有效,而 windows 則會在一定時間后返回錯誤信息。這似乎可以通過設置 SO_KEEPALIVE 選項來解決,不過不知道這個選項是否對于所有平臺都有效。

?

TIME_WAIT 狀態(tài)所帶來的影響

當某個連接的一端處于 TIME_WAIT 狀態(tài)時,該連接將不能再被使用。事實上,對于我們比較有現(xiàn)實意義的是,這個端口將不能再被使用。某個端口處于 TIME_WAIT 狀態(tài)(其實應該是這個連接)時,這意味著這個 TCP 連接并沒有斷開(完全斷開),那么,如果你 bind 這個端口,就會失敗。對于服務器而言,如果服務器突然 crash 掉了,那么它將無法再 2MSL 內重新啟動,因為 bind 會失敗。解決這個問題的一個方法就是設置 socket 的 SO_REUSEADDR 選項。這個選項意味著你可以重用一個地址。

注意:當建立一個 TCP 連接時,服務器端會繼續(xù)用原有端口監(jiān)聽,同時用這個端口與客戶端通信。而客戶端默認情況下會使用一個隨機端口與服務器端的監(jiān)聽端口通信。有時候,為了服務器端的安全性,我們需要對客戶端進行驗證,即限定某個 IP 某個特定端口的客戶端。客戶端可以使用 bind 來使用特定的端口。對于服務器端,當設置了 SO_REUSEADDR 選項時,它可以在 2MSL 內啟動并 listen 成功。 但是對于客戶端,當使用 bind 并設置 SO_REUSEADDR 時,如果在 2MSL 內啟動,雖然 bind 會成功,但是在 windows 平臺上 connect 會失敗。而在 linux 上則不存在這個問題。

?

TCP 可靠傳輸

TCP 的任務是在 IP 層的不可靠、盡力而為服務的基礎上建立一種可靠數(shù)據(jù)傳輸服務。TCP 提供的可靠數(shù)據(jù)傳輸服務就是要保證接收方進程從緩沖區(qū)讀出的字節(jié)流與發(fā)送方發(fā)出的字節(jié)流是完全一樣的。TCP 使用了校驗、序號、確認和重傳機制來達到這個目的。

校驗

在計算校驗和時,要在 TCP 數(shù)據(jù)報之前增加 12 個字節(jié)的偽首部,偽首部并不是 TCP 報文段真正的首部。只是在計算校驗和時,臨時添加在 TCP 數(shù)據(jù)報文段的前面,得到一個臨時的 TCP 報文段。偽首部既不向下傳送也不向上遞交,而僅僅是為了計算校驗和。注意:IP 數(shù)據(jù)報的校驗和只檢驗 IP 數(shù)據(jù)報的首部,但 TCP 的校驗和會把首部和數(shù)據(jù)部分一起檢驗。

序號

TCP 首部的序號字段用來保證數(shù)據(jù)能有序提交給應用層,TCP 把數(shù)據(jù)看成一個無結構但是有序的字節(jié)流,而序號是建立在傳送的字節(jié)流之上,而不是建立在報文段之上。TCP 連接中傳送的數(shù)據(jù)流中的每一個字節(jié)都編上一個序號。序號字段的值則指的是本報文段所發(fā)送的數(shù)據(jù)的第一個字節(jié)的序號。

確認

TCP 首部的確認號是期望收到對方的下一個報文段的數(shù)據(jù)的第一個字節(jié)的序號。TCP 默認使用累計確認,即 TCP 只確認數(shù)據(jù)流中至第一個丟失字節(jié)為止的字節(jié)。例如,接收方 B 收到了發(fā)送方 A 發(fā)送的包含字節(jié) 0~2 和 6~7 的報文段。由于某些原因,B 還沒有收到字節(jié) 3~5 的報文段,此時 B 仍在等待字節(jié) 3(和其后面的字節(jié)),因此,B 到 A 的下一個報文段將確認號字段設置為 3。

重傳

有兩種事件會導致 TCP 對報文段進行重傳:超時冗余 ACK

(1) 超時

TCP 每發(fā)送一個報文段,就對這個報文段設置一次計時器。只要計時器設置的重傳時間到期但還沒有收到確認,就要重傳這一報文段。當檢測到接收數(shù)據(jù)有錯誤時,會采取直接丟棄出錯的數(shù)據(jù),發(fā)送端等待接收端的確認超時后,會自動重發(fā)該報文段。

由于 IP 數(shù)據(jù)報在傳輸?shù)臅r候選擇的路由變化很大,因此傳輸層的往返時延的方差很大。為了計算超時計時器的重傳時間,TCP 采用一種自適應算法,它記錄一個報文段發(fā)出的時間,以及收到相應確認的時間,這兩個時間之差叫做報文段的往返時間(RTT)。TCP 保留了 RTT 的一個加權平均往返時間 RTTs,當?shù)谝淮螠y量 RTT 樣本時,RTTs 值就為所測量到的 RTT 樣本的值,但之后每測量一個新的 RTT 樣本,就按下式重新計算一次 RTTs:

新的 RTTs = ( 1- a ) * (舊的 RTTs) + a(新的RTT樣本)

在上式中 0 <= a < 1。若 a 很接近于零,表示新的 RTTs 值和舊的 RTTs 值相比變化不大,而受新的 RTT 樣本影響不大(RTT值更新較慢)。若 a 接近于 1,則表示新的 RTTs 值受新的 RTT 樣本的影響較大(RTT值更新較快)。[RFC 2988] 推薦的 a 值為 0.125。

所以超時計時器設置的超時重傳時間(RTO)應略大于上面得出的加權平均往返時間 RTTs。即 RTO = RTTs + 4RTTd。其中 RTTd 是 RTT 的偏差的加權平均值,它與 RTTs 和新的 RTT 樣本之差有關。當?shù)谝淮螠y量時,RTTd 取為測量到的 RTT 樣本值的一半,以后測量中,使用下式計算:新的 RTTd = (1-β) *(舊的 RTTd) + β*|RTTs - 新的 RTT 樣本|,其中 β 是個小于 1 的系數(shù),它的推薦值是 0.25。

?

(2) 冗余 ACK

超時觸發(fā)重傳存在的一個問題就是超時周期往往太長。幸運的是,發(fā)送方通常可在超時事件發(fā)生之前通過注意冗余 ACK 來較好地檢測丟包情況。冗余 ACK 就是再次確認某個報文段的 ACK,而發(fā)送方先前已經(jīng)收到過該報文段的確認。例如,發(fā)送方 A 發(fā)送了序號為 1、2、3、4、5 的 TCP 報文段,其中 2 號報文段丟失了,它將無法到達接收方 B。因此,3、4、5 號報文段對于 B 來說就成了失序報文段。TCP 規(guī)定每當比期望序號大的失序報文段到達時,發(fā)送冗余 ACK,指明下一個期望字節(jié)的序號 [RFC 1122, RFC 2581]。在這個例子中,3、4、5 號報文到達 B,但它們不是 B 所期待的下一個報文,于是 B 就發(fā)送 3 個對 1 號報文段的冗余 ACK,表示自己期望接收 2 號報文段。TCP 規(guī)定當發(fā)送方收到對同一個報文段的 3 個冗余 ACK 時,就可以認為跟在這個被確認報文段之后的報文段已經(jīng)丟失。就前面的例子而言,當 A 收到對于 1 號報文段的 3 個冗余 ACK 時,則它可以認為 2 號報文段已經(jīng)丟失。這時發(fā)送方 A 可以立即對 2 號報文執(zhí)行重傳,這種技術成為快速重傳

?

TCP流量控制

TCP 提供了流量控制服務以消除發(fā)送方使接收方緩存區(qū)溢出的可能性,因此 TCP 流量控制是為了匹配發(fā)送方的發(fā)送速率與接收方的讀取速率。TCP 提供一種基于滑動窗口協(xié)議的流量控制機制。其原理是:在通信過程中,接收方根據(jù)自己接收緩存的大小,動態(tài)地調整發(fā)送方的發(fā)送窗口大小,這就是接收窗口 rwnd,即調整 TCP 報文段首部中的 "窗口" 字段的值,來限制發(fā)送方向網(wǎng)絡注入報文的速率。同時,發(fā)送方根據(jù)其對當前網(wǎng)絡擁塞程度的估計而確定窗口值,稱為擁塞窗口 cwnd,其大小與網(wǎng)絡的帶寬和時延密切相關。

例如,在通信中,有效數(shù)據(jù)只從 A 發(fā)往 B,而 B 僅向 A 發(fā)送確認報文,這時,B 就可以通過設置確認報文段首部的窗口字段來將 rwnd 值來限制自己發(fā)送窗口的大小,這樣可以將未確認的數(shù)據(jù)量控制再 rwnd 大小之內,保證了 A 不會使 B 得接收緩存溢出。A 的發(fā)送窗口的實際大小取的是 rwnd 和 cwnd 中的最小值。

設主機 A 向主機 B 發(fā)送數(shù)據(jù),在連接建立時,B 告訴 A:"我的接收窗口 rwnd = 400(字節(jié))"。

傳輸層和數(shù)據(jù)鏈路層的流量控制的區(qū)別在于:傳輸層定義了端到端用戶之間的流量控制,數(shù)據(jù)鏈路層定義了兩個中間的相鄰結點的流量控制。另外,數(shù)據(jù)鏈路層滑動窗口協(xié)議的窗口大小不能動態(tài)變化,傳輸層則可以動態(tài)變化。

?

TCP擁塞控制

所謂的擁塞控制就是為了防止過多的數(shù)據(jù)注入網(wǎng)絡中,這樣可以使網(wǎng)絡中的路由器或鏈路不會過載。當出現(xiàn)擁塞時,端點并不能了解到擁塞發(fā)生的細節(jié),對通信連接的端點來說,擁塞往往表現(xiàn)為通信時延的增加。擁塞控制和流量控制相似的地方是通過控制發(fā)送方發(fā)送數(shù)據(jù)的速率來達到效果。

擁塞控制與流量控制的區(qū)別:擁塞控制是讓網(wǎng)絡能夠承受現(xiàn)有的網(wǎng)絡負荷,它是一個全局性的過程,涉及所有的主機、所有的路由器,以及與降低網(wǎng)絡傳輸性能有關的所有因素。而流量控制往往使指點對點通信量的控制,即接收端控制發(fā)送端,它所做的就是抑制發(fā)送端發(fā)送數(shù)據(jù)的速率,以便使接收端來得及接收。

為了更好地對傳輸層進行擁塞控制,有以下四種算法:慢開始、擁塞避免、快重傳、快恢復。發(fā)送方在確定發(fā)送報文段的速率時,既要根據(jù)接收方的接收能力,又要從全局考慮不要使網(wǎng)絡發(fā)生擁塞。因此,TCP 協(xié)議要求發(fā)送方維護以下兩個窗口:

(1) 接收窗口 rwnd,接收方根據(jù)目前接收緩存大小所許諾的最新的窗口值,反映了接收方的容量。有接收方根據(jù)其放在 TCP 報文的首部的 "窗口" 字段通知發(fā)送方。

(2) 擁塞窗口 cwnd,發(fā)送方根據(jù)自己估算的網(wǎng)絡擁塞程度而設置的窗口值,反映了網(wǎng)絡當前容量。只要網(wǎng)絡沒有出現(xiàn)擁塞,擁塞窗口就再增大一些,以便把更多的分組發(fā)送出去。但只要網(wǎng)絡出現(xiàn)擁塞,擁塞窗口就減少一些,以減少注入網(wǎng)絡中的分組數(shù)。發(fā)送窗口的上限值應當取接收窗口 rwnd 和擁塞窗口 cwnd 中較小的一個,即:Min[rwnd, cwnd]。

?

慢開始和擁塞避免

(1) 慢開始算法

在 TCP 剛剛連接好,開始發(fā)送 TCP 報文段時,先讓擁塞窗口 cwnd = 1,即一個最大報文段長度 MSS。而在每收到一個對新的報文段的確認后,將 cwnd 加倍,即剛開始會增大一個 MSS。用這樣的方法逐步增大發(fā)送方的擁塞窗口 cwnd,可以使分組注入到網(wǎng)絡的速率更加合理。例如,A 向 B 發(fā)送數(shù)據(jù),當發(fā)送時 A 的擁塞窗口為 2,那么 A 一次可以發(fā)送兩個 TCP 報文段,當經(jīng)過一個 RTT 后,A 收到 B 對剛才兩個報文的確認,于是就把擁塞窗口調整為 4,即下一次發(fā)送時就可以發(fā)送 4 個報文段。

使用慢開始算法后,每經(jīng)過一個傳輸輪次,擁塞窗口 cwnd 就會加倍,即 cwnd 的大小呈指數(shù)形式增長。這樣慢開始一直把擁塞窗口 cwnd 增大到一個規(guī)定的慢開始門限 ssthresh(閾值),然后改用擁塞避免算法。

?

(2) 擁塞避免算法

擁塞避免算法的做法是:發(fā)送端的擁塞窗口 cwnd 每經(jīng)過一個往返時延 RTT 就增加一個 MSS 的大小,而不是加倍,使 cwnd 按線性規(guī)律緩慢增長(即加法增大),而當出現(xiàn)一次超時(網(wǎng)絡擁塞)時,會令慢開始門限 ssthresh 等于當前 cwnd 的一半(即乘法減小)。

根據(jù) cwnd 的大小執(zhí)行不同的算法,可歸納為:

當 cwnd?<?ssthresh 時,使用慢開始算法。

當 cwnd?> ssthresh 時,改用擁塞避免算法。

當 cwnd?=?ssthresh 時,既可以使用慢開始也可以使用擁塞避免算法。

?

(3) 網(wǎng)絡擁塞的處理

當網(wǎng)絡出現(xiàn)擁塞時,無論是在慢開始階段還是在擁塞避免階段,只要發(fā)送方檢測到超時事件的發(fā)生(沒有按時收到確認,重傳計時器超時),就把慢開始門限?ssthresh?設置為出現(xiàn)擁塞時的發(fā)送窗口?cwnd 值的一半(但不能小于2)。然后把擁塞窗口?cwnd 重新設置為 1,執(zhí)行慢開始算法。這樣做的目的就是要迅速減少主機發(fā)送到網(wǎng)絡中的分組數(shù),使得發(fā)生擁塞的路由器有足夠的時間把隊列中積壓的分組處理完。擁塞避免是指在擁塞避免階段把擁塞窗口控制為線性規(guī)律增長,使網(wǎng)絡比較不容易出現(xiàn)擁塞,利用以上措施想要完全避免網(wǎng)絡擁塞是不可能的。

1. 初始時,擁塞窗口置為 1,即 cwnd = 1,慢開始門限置為 16,即 ssthresh = 16。慢開始階段,cwnd 初值為 1,以后發(fā)送方每收到一個確認 ACK,cwnd 值加倍,即經(jīng)過每個傳輸輪次(RTT),cwnd 呈指數(shù)規(guī)律增長。

2. 當擁塞窗口 cwnd 增長到慢開始門限 ssthresh 時(即當 cwnd = 16 時),就改用擁塞避免算法,cwnd 按線性規(guī)律增長。

3. 若此時 cwnd = 24 時,網(wǎng)絡發(fā)生擁塞,更新 ssthresh 值為 12(即變?yōu)槌瑫r的時侯 cwnd 值的一半),cwnd 重置為 1,并執(zhí)行慢開始算法,當 cwnd = 12 時,改為擁塞避免算法。

注意,在慢開始階段,若 2*cwnd > ssthresh,則下一個 RTT 的 cwnd 應等于 ssthresh,而不是 2*cwnd,即 cwnd 不能越過 ssthresh 值。如上圖,在 16 個輪次時,cwnd = 8、ssthresh = 12,第 17 輪次時,cwnd = 12,而不是 16。

?

快重傳和快恢復

(1) 快重傳

TCP 可靠傳輸機制中,快速重傳技術使用了冗余 ACK 來檢測丟包的發(fā)生。同樣,冗余 ACK 也用于網(wǎng)絡擁塞的檢測。快重傳并非取消重傳計時器,而是在某些情況下可更早的重傳丟失的報文段。當發(fā)送方連續(xù)收到三個重復的 ACK 報文時,直接重傳對方尚未收到的報文段,而不必等待那個報文段設置的重傳計時器超時。

如果收到 3 個相同的 ACK。TCP?在收到亂序到達包時就會立即發(fā)送 ACK,TCP 利用 3 個相同的 ACK 來判定數(shù)據(jù)包的丟失,此時進行快速重傳,快速重傳做的事情有:

1. 把 ssthresh 設置為 cwnd 的一半。

2. 把 cwnd 再設置為 ssthresh 的值(具體實現(xiàn)有些為 ssthresh + 3)。

3. 重新進入擁塞避免階段。

?

(2) 快恢復

后來的 "快速恢復" 算法是在上述的 "快速重傳" 算法后添加的,當收到 3 個重復 ACK 時,TCP 最后進入的不是擁塞避免階段,而是快速恢復階段。快速重傳和快速恢復算法一般同時使用。快速恢復的思想是 "數(shù)據(jù)包守恒" 原則,即同一個時刻在網(wǎng)絡中的數(shù)據(jù)包數(shù)量是恒定的,只有當 "老" 數(shù)據(jù)包離開了網(wǎng)絡后,才能向網(wǎng)絡中發(fā)送一個 "新" 的數(shù)據(jù)包,如果發(fā)送方收到一個重復的 ACK,那么根據(jù) TCP?的 ACK 機制就表明有一個數(shù)據(jù)包離開了網(wǎng)絡,于是 cwnd 加 1。如果能夠嚴格按照該原則那么網(wǎng)絡中很少會發(fā)生擁塞,事實上擁塞控制的目的也就在修正違反該原則的地方。

具體來說快速恢復的主要步驟是:

1. 當收到 3 個重復ACK時,把 ssthresh 設置為 cwnd 的一半,把 cwnd 設置為 ssthresh 的值加 3(有的實現(xiàn)版本不加 3),然后重傳丟失的報文段,加 3 的原因是因為收到 3 個重復的ACK,表明有 3 個 "老" 的數(shù)據(jù)包離開了網(wǎng)絡。

2. 再收到重復的 ACK 時,擁塞窗口增加 1。

3. 當收到新的數(shù)據(jù)包的 ACK 時,把 cwnd 設置為第一步中的 ssthresh 的值。原因是因為該 ACK 確認了新的數(shù)據(jù),說明從重復 ACK 時的數(shù)據(jù)都已收到,該恢復過程已經(jīng)結束,可以回到恢復之前的狀態(tài)了,也即再次進入擁塞避免狀態(tài)。

快速重傳算法首次出現(xiàn)在 4.3BSD 的 Tahoe 版本,快速恢復首次出現(xiàn)在 4.3BSD 的 Reno 版本,也稱之為 Reno 版的 TCP?擁塞控制算法。

在流量控制中,發(fā)送方發(fā)送數(shù)據(jù)的量由接收方?jīng)Q定,而在擁塞控制中,有發(fā)送方自己通過檢測網(wǎng)絡狀況而決定。實際上,慢開始、擁塞避免算法、快重傳和快恢復集中算法應該是同時應用在擁塞控制機制之中的,當發(fā)送方檢測到超時的時候就采用慢開始和擁塞避免,當發(fā)送方接收到冗余 ACK 的時候就采用快重傳和快恢復。

?

面試相關問題

(1) 為什么四次揮手發(fā)送最后一次報文后要等待 2MSL(報文最大生存時間)的時間?

答:1) 為了保證 A 發(fā)送的最后一個確認報文段能夠到達 B。如果 A 不等待 2MSL,若 A 返回的最后確認報文段丟失,則 B 不能進入正常關閉狀態(tài),而 A 此時已經(jīng)關閉,也不可能再重傳。

2)?防止出現(xiàn) "已失效的連接請求報文段"。A 在發(fā)送完最后一個確認報文段后,再經(jīng)過 2MSL 可保證本連接持續(xù)的時間內所產生的所有報文段從網(wǎng)絡中消失。

?

(2) TCP 使用的是 GBN(后退N幀協(xié)議)還是 SR(選擇重傳協(xié)議)?

答:這是為了人踩坑而出的問題。因為?TCP 使用累計確認,看起來像是 GBN。但是,正確收到但失序的報文并不會被丟棄,而是緩存起來,并且發(fā)送冗余 ACK 指明希望收到的下一個報文段,這是 TCP 方式和 GBN 的顯著區(qū)別。例如,A 發(fā)送了 N 個報文段,其中第 k(k < N)個報文段丟失,其余 N-1 個報文段正確地按序到達接收方 B。當使用 GBN 時,A 需要重傳分組 k,以及所有后繼分組 k+1,k+2,...,N。相反,TCP 卻最多重傳一個報文段,即報文段 k。另外,TCP 中提供一個 SACK(Selective ACK)選項,也就是選擇確認選項。當使用選擇確認選項的時候, TCP 看起來就和 SR 非常相似。因此,TCP 的差錯恢復機制可以看成是 GBN 和 SR 協(xié)議的混合體。

?

(3) 為什么超時時間發(fā)生時 cwnd 被置為 1,而收到 3 個冗余 ACK 時 cwnd 只是減半?

答:首先應分析那種情況的網(wǎng)絡擁塞程度更嚴重。其實不難發(fā)現(xiàn),在收到 3 個冗余 ACK 的情況下,網(wǎng)絡雖然擁塞,但至少 ACK 報文段能夠被正確交付。而當超時發(fā)生時,說明網(wǎng)絡可能已經(jīng)擁塞的連 ACK 報文段都傳輸不了了,發(fā)送方只能等待超時后重傳數(shù)據(jù)。因此,超時時間發(fā)生時,網(wǎng)絡擁塞更嚴重,所以發(fā)送方應該最大限度地抑制數(shù)據(jù)發(fā)送量,所以 cwnd 置為 1;收到 3 個冗余 ACK 時,網(wǎng)絡擁塞相對而言不是很嚴重,所以 cwnd 減半即可。

?

(4) 為什么不采用 "兩次握手" 建立連接?

答:這主要是為了防止兩次握手情況下已失效的連接請求報文段突然有傳送到服務端,而產生了錯誤。考慮以下情況:客戶 A 想服務器 B 發(fā)送 TCP 連接請求,第一個連接請求報文在網(wǎng)絡的某個結點長時間滯留,A 超時后認為報文丟失,于是再重傳一次連接請求,B 收到后建立連接。數(shù)據(jù)傳輸完畢后雙方斷開連接。此時,前一個滯留在網(wǎng)絡中的連接請求到達了服務端 B,而 B 認為 A 有發(fā)來連接請求,此時若是使用 "三次握手",則 B 向 A 返回確認報文段,由于是一個失效的請求,因此 A 不予理睬,建立連接失敗。若采用的是 "兩次握手",則這種情況下 B 認為傳輸連接已經(jīng)建立,并一直等待 A 傳輸數(shù)據(jù),而 A 此時并無連接請求,因此不予理睬,這樣就造成了 B 的資源白白浪費了。

?

(5) 是否 TCP 和 UDP 都需要計算往返時間 RTT?

答:往返時間 RTT 只是針對傳輸層 TCP 協(xié)議才很重要,因為 TCP 要根據(jù) RTT 的值來設置超時計時器的超時時間。UDP 沒有確認和重傳機制,因此 RTT 對 UDP 沒有什么意義。

?

(6) 為什么 TCP 在建立連接的時候不能每次選擇相同的、固定的初始序號?

答:1) 假如 A 和 B 頻繁地建立連接,傳送一些 TCP 報文段后再釋放連接,然后又不斷的建立新的連接、傳送報文段和釋放連接。

2) 假如每一次建立連接時,主機 A 都選擇相同的、固定的初始序號,如 1。

3) 若主機 A 發(fā)送出的某些 TCP 報文段在網(wǎng)絡中會滯留較長的時間,以致造成主機 A 超時重傳這些 TCP 報文段。

4) 若有一些在網(wǎng)絡中滯留時間較長的 TCP 報文段最后終于到達了主機 B,但這時傳送該報文段的那個連接早已釋放了,而在到達主機 B 時的 TCP 連接是一條新的 TCP 連接。

以上這些情況可能會導致在新的 TCP 連接中的主機 B 有可能會接收在舊的連接傳送的、已經(jīng)沒有意義的、過時的 TCP 報文段(因為這個 TCP 報文段的序號有可能正好處于新的連接所使用的序號范圍內)。因為必須使得遲到的 TCP 報文段的序號不在新的連接中使用的序號范圍內。所以,TCP 在建立新的連接時所選擇的初始序號一定要和前面的一些連接所使用過的序號不一樣。因此,不同的 TCP 連接不能使用相同的初始序號。

?

(7) 在使用 TCP 傳輸數(shù)據(jù)時,如果有一個確認報文段丟失了,也不一定會引起與該確認報文段對應的數(shù)據(jù)的重傳。試說明理由。

答:這是因為發(fā)送方可能還未重傳時,就收到了更高序號的確認。例如主機 A 連續(xù)發(fā)送兩個報文段,均正確到達主機 B。B 連續(xù)發(fā)送兩個確認 ACK1 和 ACK2(ACK2 的序號比 ACK1 的序號高)。但前一個確認幀在傳輸時丟失了。若在超時前,ACK2 被 A 接收,更高的序號代表該序號之前的所有字節(jié)都被接收了,所以 A 知道前一個報文也被正確的接收了,這種情況下 A 不會重傳第一個報文段。

?

參考:《計算機網(wǎng)絡》《Linux高性能服務器編程》

總結

以上是生活随笔為你收集整理的TCP 可靠传输机制详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內容還不錯,歡迎將生活随笔推薦給好友。