傳輸控制協議 TCP
傳輸控制協議 TCP 概述
TCP 最主要的特點
- TCP 是
面向連接的運輸層協議
。應用程序在使用 TCP 協議之前,必須先建立 TCP 連接。在傳送數據完畢後,必須釋放已經建立的 TCP 連接 - 每一條 TCP 連接只能有兩個
端點
,每一條 TCP 連接只能是點對點
的(一對一) - TCP 提供
可靠交付
的服務。通過 TCP 連接傳送的數據,無差錯、不丟失、不重複,並且按序到達 - TCP 提供
全雙工通信
。TCP 允許通信雙方的應用進程在任何時候都能發送數據。TCP 連接的兩端都設有發送緩存和接受緩存,用來臨時存放雙向通信的數據 面向字節流
。TCP 中的“流”指的是流入到進程或從進程流出的字節序列
面向字節流
“面向字節流”的含義是:雖然應用程序和 TCP 的交互式一次一個數據塊(大小不等),但 TCP 把應用程序交下來的數據僅僅看成是一連串的無結構的字節流
。TCP 並不知道所傳送的字節流的含義
TCP 不保證接收方應用程序所收到的數據塊和發送方應用程序所發出的數據塊具有對應大小的關係
例如,發送方應用程序交給發送方的 TCP 共10個數據塊,但接收方的 TCP 可能只用了4個數據塊就把收到的字節流交付上層的應用程序
接收方應用程序收到的字節流必須和發送方應用程序發出的字節流完全一樣。接收方的應用程序必須有能力識別收到的字節流,把它還原成有意義的應用層數據
TCP 和 UDP 在發送報文時採用的方式完全不同。TCP 並不關心應用進程一次把多長的報文發送到 TCP 的緩存中,而是根據對方給出的窗口值和當前網絡擁塞的程度來決定一個報文段應包含多少個字節(UDP 發送的報文長度是應用進程給出的)。如果應用進程傳送到 TCP 緩存的數據塊太長,TCP 就可以把它劃分短一些再傳送。如果應用進程一次只發來一個字節,TCP 也可以等待積累有足夠多的字節後再構成報文段發送出去
TCP 的連接
TCP 把連接
作爲最基本的抽象
。TCP 的許多特性都與 TCP 是面向連接的這個基本特性有關
TCP 連接的端點叫做套接字(socket)或插口
,根據 RFC 793 的定義:端口號拼接到(concatenated with) IP 地址即構成了套接字
套接字 socket = (IP 地址:端口號)
每一條 TCP 連接唯一地被通信兩端的兩個端點(即兩個套接字)所確定
TCP 連接 ::= {socket1, socket2} = {(IP1: port1), (IP2: port2)}
TCP 連接就是由協議軟件所提供的一種抽象。TCP 連接的端口是個很抽象的套接字
,即( IP地址
: 端口號
)。同一個 IP 地址可以有多個不同的 TCP 連接,而同一個端口號也可以出現在多個不同的 TCP 連接中
易混淆的 socket
同一個名詞 socket 卻可表示多種不同的意思,以下 socket 的意思跟本文中所引用的 RFC 793 定義的 socket(指端口號拼接到 IP 地址)不同
- 允許應用程序訪問連網協議的
應用編程接口 API(Application Programming Interface)
,即運輸層和應用層之間的接口,稱爲 socket API,並簡稱爲 socket - 在 socket API 中使用的一個
函數名
也叫做 socket - 調用 socket 函數的
端點
稱爲 socket,如“創建一個數據報 socket” - 調用 socket 函數時,其
返回值
稱爲 socket 描述符,可簡稱爲 socket - 在操作系統內核中連網協議的 Berkeley 實現,稱爲 socket
實現
可靠傳輸的工作原理
理想的傳輸條件
理想的傳輸條件有以下兩個特點
- 傳輸信道不產生差錯
- 不管發送方以多快的速度發送數據,接收方總是來得及處理收到的數據
實際的網絡不具備以上兩個理想條件。需要使用一些可靠的傳輸協議,當出現差錯時讓發送方重傳出現差錯的數據,同時在接收方來不及處理收到的數據時,及時告訴發送方適當減低發送數據的速度。這樣,不可靠的傳輸信道就能夠實現可靠傳輸了
停止等待協議
全雙工通信的雙方既是發送方也是接收方。把傳送的數據單元都稱爲分組。“停止等待”就是每發完一個分組就停止發送,等待對方的確認。在收到確認後再發送下一個分組
無差錯情況
出現差錯
只要超過一段時間沒有收到確認,就認爲剛纔發送的分組丟失了,因而重傳前面發送過的分組。這就叫做超時重傳
。要實現超時重傳,就要在每發送完一個分組時設置一個超時計時器
- 發送完一個分組後,
必須暫時保留已發送的分組的副本
(在發生超時重傳時使用)。只有在收到相應的確認後才能清除暫時保留的分組副本 - 分組和確認分組都必須進行
編號
。這樣才能明確是哪一個發送出去的分組收到了確認,而哪一個分組還沒有收到確認 - 超時計時器的重傳時間
應當比數據在分組傳輸的平均往返時間更長一些
確認丟失和確認遲到
使用上述的確認和重傳機制,我們就可以在不可靠的傳輸網絡上實現可靠的通信
像上述的這種可靠傳輸協議常稱爲自動重傳請求 ARQ(Automatic Repeat reQuest)
。重傳的請求是自動進行的。接收方不需要請求發送方重傳某個出錯的分組
信道利用率
停止等待協議的優點是簡單,但缺點是信道利用率太低
爲了提高傳輸效率,發送方可以不使用低效率的停止等待協議,而是採用流水線傳輸
。流水線傳輸就是發送方可連續發送多個分組,不必每發完一個分組就停頓下來等待對方的確認。這樣可使信道上一直有數據不間斷地在傳送。這種傳輸方式可以獲得很高的信道利用率
連續 ARQ 協議
位於發送窗口內的5個分組都可以連續發送出去,而不需要等待對方的確認。可以提高信道利用率
接收方一般都是採用累積確認
的方式。接收方不需要對收到的分組逐個發送確認,而是在收到幾個分組後,對按序到達的最後一個分組發送確認
積累確認有優點也有缺點。優點是:容易實現,即使確認丟失也不必重傳。缺點是不能向發送方反映出接收方已經正確收到的所有分組的信息
TCP 報文段的首部格式
TCP 雖然是面向字節流的,但 TCP 傳送的數據單元卻是報文段。一個 TCP 報文段分爲首部和數據兩部分。TCP 報文段首部的前20個字節是固定的,後面有4n字節是根據需要而增加的選項(n是整數)。因此 TCP 首部的最小長度是20字節
首部字段
源端口
和目的端口
各佔2個字節,分別寫入源端口號和目的端口號序號
佔4字節。序號範圍是[0, 232-1],共232(即4 294 967 296)個序號。序號增加到232-1後,下一個序號就又回到0。在一個 TCP 連接中傳送的字節流中的每一個字節都按順序編號
確認號
佔4字節,是期望收到對方下一個報文段的第一個數據字節的序號
數據偏移
佔4字節,它指出 TCP 報文段的數據起始處距離 TCP 報文段的起始處有多遠。這個字段實際上是指出 TCP 報文段的首部長度保留
佔6位,保留爲今後使用,但目前應置爲0
下面有6個控制位
,用來說明本報文段的性質
-
緊急 URG(URGent)
當 URG=1 時,表明緊急指針字段有效。它告訴系統此報文段中有緊急數據,應儘快傳送(相當於高優先級的數據),而不是按原先的排隊順序來傳送 -
確認 ACK(ACKnowledgment)
僅當 ACK=1 時確認號字段纔有效。當 ACK=0 時,確認號無效。TCP 規定,在連接建立後所有傳送的報文段都必須把 ACK 置1 -
推送 PSH(Push)
當兩個應用進程進行交互式的通信時,有時在一端的應用進程希望在鍵入一個命令後立即就能夠收到對方的響應 -
復位 RST(ReSeT)
當 RST=1 時,表明 TCP 連接中出現嚴重差錯(如由於主機崩潰或其他原因),必須釋放連接,然後再重新建立運輸連接 -
同步 SYN(SYNnchronization)
在連接建立時用來同步序號。當 SYN=1 而 ACK=0 時,表明這是一個連接請求報文段。對方若同意建立連接,則應在響應的報文段中使 SYN=1 和 ACK=1 -
終止 FIN(FINis)
用來釋放一個連接。當 FIN=1 時,表明此報文段的發送發的數據已發送完畢,並要求釋放運輸連接 -
窗口
佔2字節。窗口值是[0, 216-1]之間的整數。窗口值作爲接收方讓發送方設置其發送窗口的依舊 -
檢驗和
佔2字節。檢驗和字段檢驗的範圍包括首部和數據這兩部分 -
緊急指針
佔2字節。緊急指針僅在 URG=1 時纔有意義,它指出本報文段中的緊急數據的字節數 -
選項
長度可變,最長可達40字節
TCP 可靠傳輸的實現
以字節爲單位的滑動窗口
發送窗口構造
TCP 的滑動窗口是以字節爲單位的。假定 A 收到了 B 發來
的確認報文段,其中窗口是20字節,而確認號是31(這表明 B 期望收到的下一個序號是31,而序號30爲止的數據已經收到了)。根據這兩個數據,A 就構造出自己的發送窗口
發送窗口標識:在沒有收到 B 的確認的情況下,A 可以連續把窗口內的數據都發送出去。凡是已經發送出去的數據,在未收到確認之前都必須暫時保留,以便在超時重傳時使用
發送窗口變化
發送窗口的位置由窗口前沿和後沿的位置共同確定。發送窗口後沿的變化情況有兩種,即不動(沒有收到新的確認)和前移(收到了新的確認)。發送窗口後沿不可能向後移動,因爲不能撤銷已收到的確認
發送窗口前沿通常是不斷向前移動,但也有可能不動。這對應於兩種情況:
- 一是沒有收到新的確認,對應通知的窗口大小也不變
- 二是收到了新的窗口單對方通知的窗口縮小了,使得發送窗口前沿正好不動
發送窗口前沿也有可能向後收縮
。這發生在對方通知的窗口縮小了。但 TCP 的標準強烈不贊成這樣做
。因爲很可能發送方在收到這個通知以前已經發送了窗口中的許多數據,現在又要收縮窗口,不讓發送這些數據,這樣就會產生一些錯誤
要描述一個發送窗口的狀態需要三個指針:P1,P2,P3。指針都指向字節的序號。這三個指針指向的幾個部分的意義如下:
- 小於 P1 的是已發送並已收到確認的部分,而大於 P3 的是不允許發送的部分
- P3 - P1 =
A 的發送窗口
- P2 - P1 已發送但尚未收到確認的字節數
- P3 - P2 允許發送但當前尚未發送的字節數(又稱爲
可用窗口
或有效窗口
)
B 的接收窗口大小是20。在接收窗口外面,到30號爲止的數據是已經發送過確認,並且已經交付主機了。因此在 B 可以不再保留這些數據。接收窗口內的序號(31~50)是允許接收的。在上圖中,B 收到了序號爲32和33的數據。這些數據沒有按序到達,因爲序號爲31的數據沒有收到(也許丟失了,也許滯留在網絡中的某處)。請注意,B 只能對按序收到的數據中的最高序號給出確認,因此 B 發送的確認報文段中的確認號仍然是31(即期望收到的序號),而不是32或33
現在假定 B 收到了序號爲31的數據,並把序號爲31~33的數據交付主機,然後 B 刪除這些數據。接着把接收窗口向前移動3個序號,同時給 A 發送確認,其中窗口值仍爲20,但確認號是34。這表明 B 已經收到了到序號33爲止的數據。B 還收到了序號爲37,38和40的數據,但這些都沒有按序到達,只能先暫存在接收窗口中。A 收到 B 的確認後,就可以把發送窗口向前滑動3個序號,但指針 P2 不動。現在 A 的可用窗口增大了,可發送的序號範圍是42~53
A 在繼續發送完序號42~53的數據後,指針 P2 向前移動和 P3 重合。發送窗口內的序號都已用完,但還沒有再收到確認。由於 A 的發送窗口已滿,可用窗口已減小到零,因此必須停止發送。發送窗口內所有的數據都已正確到達 B,B 也早已發出了確認。但所有這些確認都滯留在網絡中。在沒有收到 B 的確認時,A 不能猜測:”或許 B 收到了吧!“爲了保證可靠傳輸,A 只能認爲 B 還沒有收到這些數據。於是,A 在經過一段時間後(由超時計時器控制)就重傳這部分數據,重新設置超時計時器,直到收到 B 的確認爲止。如果 A 收到確認號落在發送窗口內,那麼 A 就可以發送窗口繼續向前滑動,併發送新的數據
緩存和窗口
發送方維持的發送緩存和發送窗口,以及接收方維持的接收緩存和接收窗口
發送緩存用來暫時存放:
- 發送應用程序傳送給對方 TCP 準備發送的數據
- TCP 已發送出但尚未收到確認的數據
已被確認的數據應當從發送緩存中刪除,因此發送緩存和發送窗口的後沿是重合的。發送應用程序必須控制寫入緩存的速率,不能太快,否則發送緩存就會沒有存放數據的空間
接收緩存用來暫時存放:
- 按序到達的、但尚未被接收應用程序讀取的數據
- 未按序到達的數據
收到的分組被檢測出有差錯,則丟棄。接收應用程序來不及讀取收到的數據,接收緩存最終就會被填滿,使接收窗口減小到零。接收應用程序能夠及時從接收緩存中讀取收到的數據,接收窗口就可以增大,最大亦不能超過接收緩存的大小
要點小結:
- 雖然 A 的發送窗口是根據 B 的接收窗口設置的,但在同一時刻,A 的發送窗口並不總是和 B 的接收窗口一樣大。通過網絡傳送窗口值需要經歷一定的時間滯後,該時間並不確定的
- 對於不按序到達的數據,TCP 通常是先臨時存放在接收窗口,等字節流中所缺少的字節收到後,在
按序交付上層的應用進程
- TCP 要求接收方必須有累積確認的功能,這樣可以減少傳輸開銷
超時重傳時間的選擇
TCP 的發送方在規定的時間內沒有收到確認就要重傳已發送的報文段。這種重傳的概念是很簡單的,但重傳時間的選擇卻是 TCP 最複雜的問題之一
由於 TCP 的下層是互聯網環境,發送的報文段可能只經過一個高速率的局域網,也可能經過多個低速率的網絡,並且每個 IP 數據報所選擇的路由還可能不同。如果把超時重傳時間設置得太短,就會引起很多報文段的不必要的重傳,使網絡負荷增大。但若把超時重傳時間設置的過長,則又使網絡的空閒時間增大,降低了傳輸效率
TCP 採用了一種自適應算法,它記錄一個報文段發出的時間,以及收到相應的確認的時間。這兩個時間之差就是報文段的往返時間 RTT
新的 RTTs = (1 - α) x (舊的 RTTs) + α x (新的 RTT 樣本)
RTT:報文段往返時間
RTTs:加權平均往返時間
α: 0 ≤ α < 1,RFC 6298 推薦的 α 值爲 1/8,即 0.125
RTO = RTTs + 4 x RTTD
RTO:超時重傳時間
RTTD:RTT 的偏差的加權平均值
新的 RTTD = (1 - β) x (舊的 RTTD) + β x |RTTs - 新的 RTT 樣本|
β:小於1的係數,推薦值是 1/4,即 0.25
TCP 流量控制
利用滑動窗口實現流量控制
流量控制(flow control):讓發送方的發送速率不要太快,要讓接收方來得及接收
利用滑動窗口機制可以很方便地在 TCP 連接上實現對發送方的流量控制
發送方的發送窗口不能超過接收方給出的接收窗口的數值
。TCP 的窗口單位是字節,不是報文段
避免死鎖:TCP 爲每一個連接設有一個持續計時器(persistence timer)
。只要 TCP 連接的一方收到對方的零窗口通知,就啓動持續計時器。若持續計時器設置的時間到期,就發送一個零窗口探測報文段
(僅攜帶1字節的數據),而對方就在確認這個探測報文段時給出了現在的窗口值。如果窗口仍是零,那麼收到這個報文段的一方就重新設置持續計時器。如果窗口不是零,那麼死鎖的僵局就可以打破了
TCP 的傳輸效率
發送機制
- TCP 維持一個變量,它等於
最大報文段長度 MSS
。只要緩存中存放的數據達到 MSS 字節時,就組裝成一個 TCP 報文段發送出去 - 由發送方的應用進程指明要求發送報文段,即 TCP 支持的
推送(push)
操作 - 發送方的一個計時器期限到了,這時把當前已有的緩存數據裝入報文段(但長度不能超過 MSS)發送出去
Nagle 算法
在 TCP 的實現中廣泛使用 Nagle 算法
若發送應用進程把要發送的數據逐個字節地送到 TCP 的發送緩存,則發送方就把第一個數據字節先發送出去,把後面到達的數據字節都緩存起來。當發送方收到對第一個數據字符的確認後,再把發送緩存中的所有數據組裝成一個報文段發送出去,同時繼續對隨後到達的數據進行緩存。只有在收到對前一個報文段的確認後才繼續發送下一個報文段。當數據達到較快而網絡速率較慢時,用這樣的方法可明顯地減少所用的網絡寬帶。Nagle 算法還規定,當到達的數據已達到發送窗口大小的一半或已達到報文段的最大長度時,就立即發送一個報文段。這樣可以有效提高網絡的吞吐量
糊塗窗口綜合徵
TCP 接收方的緩存已滿,僅剩一個字節,並還將保持這種狀態持續一段時間。導致發送方只能發送一個字節。導致網絡的效率很低
爲了解決這個問題,可以讓接收方等待一段時間
,使得或者接受緩存已有足夠空間容納一個最長的報文段,或者等到接受緩存已有一半空閒的空間
。只要出現這兩種情況之一,接收方就發出確認報文,並向發送方通知當前的窗口大小。發送方也不要發送大小的報文段,而是把數據積累成足夠大的報文段,或達到接收方緩存的空間的一半大小
TCP 的擁塞控制
擁塞控制的一般原理
在計算機網絡中的鏈路容量(即寬帶)、交換結點中的緩存和處理機等,都是網絡資源。在某段時間,若對網絡中某一資源的需求超過了該資源所能提供的可用部分,網絡的性能就要變壞。這種情況就叫做擁塞
(congestion)
擁塞控制
就是防止過多的數據注入到網絡中,這樣可以使網絡中的路由器或鏈路不致過載
。擁塞控制所要做的都是一個前提,就是網絡能夠承受現有的網絡負荷
TCP 的擁塞控制方法
TCP 進行擁塞控制的算法有四種,即慢開始
(slow-start)、擁塞避免
(congestion avoidance)、快重傳
(fast retransmit)和快恢復
(fast recovery)
慢開始
當主機開始發送數據時,由於並不清楚網絡的負荷情況,如果立即把大量數據字節注入到網絡,就有可能引起網絡發生擁塞。經驗證明,較好的方法是先探測一下,即由小到大逐漸增大發送窗口
,也就是說,由小到大逐漸增大擁塞窗口數值
cwnd:發送方的擁塞窗口,開始發送方設置 cwnd = 1
擁塞避免
讓擁塞窗口 cwnd 緩慢地增大,即每經過一個往返時間 RTT 就把發送方的擁塞窗口 cwnd 加1,而不是像慢開始階段那樣加倍增加。因此在擁塞避免階段就有“加法增大
” AI(Additive Increase)的特點。這表明在擁塞避免階段,擁塞窗口 cwnd 按線性規律緩慢增長
,比慢開始算法的擁塞窗口增長速率緩慢得多
“擁塞避免”並非完全能夠避免擁塞,而是把擁塞窗口控制爲按線性規律增長,使網絡比較不容易出現擁塞
在執行慢開始算法時,發送方每收到一個對新報文段的確認 ACK,就把擁塞窗口值加1,然後開始下一輪的傳輸。因此擁塞窗口 cwnd 隨着傳輸輪次按指數規律增長。當擁塞窗口 cwnd 增長到慢開始門限值 ssthresh 時,就改成執行擁塞避免算法,擁塞窗口按線性規律增長
ssthresh:慢開始門限,一般的,會有一個初始值,下圖中爲16個報文段
當擁塞窗口 cwnd = 24 時,網絡出現了超時,發送方判斷爲網絡擁塞。於是調整門限值 ssthresh = cwnd / 2 = 12,同時設置擁塞窗口 cwnd = 1,進入慢開始階段
快重傳
採用快重傳算法可以讓發送方儘早知道發生了個別報文段的丟失
。快重傳算法首先要求接收方不要等待自己發送數據時才進行捎帶確認,而是要立即發送確認
,即使收到了失序的報文段
也要立即發出對已收到的報文段的重複確認
快恢復
發送方知道當前只是丟失了個別的報文段。於是不啓動慢開始,而是執行快恢復
算法。這時,發送方調整門限值 ssthresh = cwnd / 2 = 8,同時設置擁塞窗口 cwnd = ssthresh = 8,並開始執行擁塞避免算法
TCP Reno 版本:區別於老的 TCP Tahao 版本
TCP 的運輸連接管理
TCP 是面向連接的協議。運輸連接是用來傳送 TCP 報文的。TCP 運輸連接的建立和釋放是每一次面向連接的通信中必不可少的過程。運輸連接有三個階段,連接建立
、數據傳送
和連接釋放
。運輸的連接管理就是使運輸連接的建立和釋放都能夠正常地進行
在 TCP 連接建立過程中要解決以下三個問題:
- 要使每一方能夠確知對方的存在
- 要允許雙方協商一些參數(最大窗口值、是否使用窗口擴大選項和時間戳選項以及服務質量等)
- 能夠對運輸實體資源(緩存大小、連接表中的項目等)進行分配
TCP 的連接建立
TCP 建立連接的過程叫做握手,握手需要在客戶和服務器之間交換三個 TCP 報文段
連接建立過程
- 最初客戶/服務器的 TCP 進程都處於
CLOSED(關閉)
狀態。在本實例中,A主動打開連接
,而 B被動打開連接
- B 的 TCP 服務器進程先創建
傳輸控制塊
TCB,並處於LISTEN(收聽)
狀態,等待客戶的連接請求 - A 的 TCP 客戶進程創建
傳輸控制模塊
TCB。並向 B 發出連接請求報文段,首部中的同部位 SYN = 1,選擇一個初始序號 seq = x。TCP 客戶端進程進入SYN-SENT(同步已發送)
狀態。TCP 規定,SYN 報文段(即 SYN = 1 的報文段)不能攜帶數據,但要消耗一個序號
- B 收到連接請求報文段後,如同意建立連接,則向 A 發送確認。在確認報文段中應把 SYN 位和 ACK 位都置1,確認號是 ack = x + 1,同時也爲自己選擇一個初始序號 seq = y。這時 TCP 服務器進程進入
SYN-RCVD(同步收到)
狀態。這個報文段也不能攜帶數據,但同樣要消耗掉一個序號
- TCP 客戶進程收到 B 的確認後,還要向 B 給出確認。確認報文段的 ACK 置1,確認號 ack = y + 1,而自己的序號 seq = x + 1。TCP 的標準規定,ACK 報文段可以攜帶數據。但
如果不攜帶數據則不消耗序號
,在這種情況下,下一個數據報文段的序號仍是 seq = x + 1。這時,TCP 連接已經建立,A 進入ESTABLISHED(已建立連接)
狀態 - 當 B 收到 A 的確認後,也進入
ESTABLISHED
狀態
傳輸控制塊
TCB(Transmission Control Block)存儲了每一個連接中的一些重要信息,如:TCP 連接表,指向發送和接收緩存的指針,指向重傳隊列的指針,當前的發送和接收序號等等
四報文握手
B 發送給 A 的報文段,可拆成兩個報文段。先發送一個確認報文段(ACK = 1,ack = x + 1),然後再發送一個同步報文段(SYN = 1,seq = y)。這樣的過程就變成了四報文握手
,與三報文握手效果一致
異常情況
爲什麼 A 最後還要發送一次確認呢?這主要是爲了防止已失效的連接請求報文段突然又傳到了 B,因而產生錯誤
正常情況:A 發出連接請求,但因連接請求報文丟失而未收到確認。於是 A 再重傳一次連接請求。後來收到了確認,建立了連接。數據傳輸完畢後,就釋放了連接。A 共發送了兩個連接請求報文段,其中第一個丟失,第二個到達了 B,沒有“已失效的連接請求報文段”
異常情況:A 發出的第一個連接請求報文段並沒有丟失,而是在某些網絡結點長時間滯留了,以致延誤到連接釋放以後的某個時間纔到達 B。本來這是一個早已失效的報文段。但 B 收到此失效的連接請求報文段後,就誤認爲是 A 又發出一次新的連接請求。於是就向 A 發出確認報文段,同意建立連接。假定不採用報文握手,那麼只要 B 發出確認,新的連接就建立了。
現在 A 並沒有發出建立連接的請求,因此不會理睬 B 的確認,也不會向 B 發送數據。但 B 卻以爲新的運輸連接已經建立了,並一直等待 A 發來數據。B 的
許多資源就這樣被浪費了
。
採用三報文握手的辦法,可以防止上述現象的發生
TCP 的連接釋放
連接釋放過程
- A 的應用進程先向其 TCP 發出連接釋放報文段,並停止再發送數據,主動關閉 TCP 連接。A 把連接釋放報文段首部的終止控制位 FIN 置1,其序號 seq = u,它等於前面已傳送過的數據的最後一個字節的序號加1。這時 A 進入
FIN-WAIT-1(終止等待1)
狀態,等待 B 的確認。TCP 規定,FIN 報文段即使不攜帶數據,也消耗一個序號 - B 收到連接釋放報文段後即發出確認,確認號是 ack = u + 1,而這個報文段自己的序號是 v,等於 B 前面已傳送過的數據的最後一個字節的序號加1。B隨即進入
CLOSE-WAIT(關閉等待)
狀態。TCP 服務器進程這時應通知高層應用進程,因而從 A 到 B 這個方向的連接就釋放了,這時的 TCP 連接處於半關閉(half-close)
狀態,即 A 已經沒有數據要發送了,但 B 若發送數據,A 仍要接收。也就是說,從 B 到 A 這個方向的連接並未關閉,這個狀態可能會持續一段時間 - A 收到來自 B 的確認後,就進入
FIN-WAIT-2(終止等待2)
狀態,等待 B 發出的連接釋放報文段 - 若 B 已經沒有要向 A 發送的數據,其應用進程就通知 TCP 釋放連接。這時 B 發出的連接釋放報文段必須使 FIN = 1。現假定 B 的序號爲 w(在半關閉狀態 B 可能又發送了一些數據)。B 還必須重複上次已發送過的確認號 ack = u + 1。這時 B 就進入
LAST-ACK(最後確認)
狀態,等待 A 的確認 - A 在收到 B 的連接釋放報文段後,必須對此發出確認。在確認報文段中把 ACK 置1,確認號 ack = w + 1,而自己的序號是 seq = u + 1(根據 TCP 標準,前面發送過的 FIN 報文段要消耗一個序號)。然後進入到
TIME-WAIT(時間等待)
狀態。此時 TCP 連接還沒有釋放掉。必須經過時間等待計時器(TIME-WAIT timer)
設置的時間2MSL後,A 才進入到CLOSED
狀態 - 當 A 撤銷相應的傳輸控制塊 TCB 後,就結束了這次的 TCP 連接
時間 MSL 叫做
最長報文段壽命
(Maximum Segment Lifetime),RFC 793建議設爲2分鐘。但這完全是從工程上來考慮的,對於現在的網絡,MSL = 2分鐘可能太長了一些
TIME-WAIT 等待時間
爲什麼 A 在 TIME-WAIT 狀態必須等待 2MSL 的時間呢?
爲了保證 A 發送的最後一個 ACK 報文段能夠到達 B。這個 ACK 報文段有可能丟失,因而使處在 LAST-ACK 狀態的 B 收不到對已發送的 FIN + ACK 報文段的確認。B 會超時 重傳這個 FIN + ACK 報文段,而 A 就能在 2MSL 時間內收到這個重傳的 FIN + ACK 報文段。接着 A 重傳一次確認,重新啓動 2MSL 計時器。最後,A 和 B 都正常進入到 CLOSED 狀態。如果 A 在 TIME-WAIT 狀態不等待一段時間,而是在發完 ACK 報文段後立即釋放連接,那麼就無法收到 B 重傳的 FIN + ACK 報文段,因而也不會再發送一次確認報文段。這樣,B 就無法安裝正常步驟進入 CLOSED 狀態
防止前面提到的“已失效的連接請求報文段”出現在本連接中。A 在發送完最後一個 ACK 報文段後,再經過時間 2MSL,就可以使本連接持續的時間內所產生的所有報文段都從網絡中消失。這樣就可以使下一個新的連接中不會出現這種舊的連接請求報文段
B 只要收到 A 發出的確認,就進入 CLOSED 狀態。同樣,B 在撤銷相應的傳輸控制 TCB 後,就結束了這次的 TCP 連接。B 結束 TCP 連接的時間要比 A 早一些
保活計時器(keepalive timer)
:服務器每收到一次客戶的數據,就重新設置保活計時器,時間的設置通常是兩小時。若兩小時沒有收到客戶的數據,服務器就發送一個探測報文段,以後則每隔75秒發送一次。若一連發送10個探測報文段後仍無客戶的響應,服務器就認爲客戶端出了故障,接着就關閉這個連接
TCP 的有限狀態機
爲了更清晰地看出 TCP 連接的各種狀態之間的關係,下圖爲 TCP 的有限狀態機。圖中每一個方框即 TCP 可能具有的狀態。每個方框中的大寫英文字符串是 TCP 標準所使用的 TCP 連接狀態名。狀態之間的箭頭表示可能發生的狀態變遷。箭頭旁邊的字,表明引起這種變遷的原因,或表明發生狀態變遷後又出現什麼動作。請注意圖中有三種不同的箭頭。粗實線箭頭
表示對客戶進程的正常變遷
。粗虛線箭頭
表示對服務器進程的正常變遷
。另一種細線箭頭
表示異常變遷