傳輸控制協定 TCP 概述
TCP 最主要的特點
- TCP 是
。應用程式在使用 TCP 協定之前,必須先建立 TCP 連接配接。在傳送資料完畢後,必須釋放已經建立的 TCP 連接配接面向連接配接的運輸層協定
- 每一條 TCP 連接配接隻能有兩個
,每一條 TCP 連接配接隻能是端點
的(一對一)點對點
- TCP 提供
的服務。通過 TCP 連接配接傳送的資料,無差錯、不丢失、不重複,并且按序到達可靠傳遞
-
。TCP 允許通信雙方的應用程序在任何時候都能發送資料。TCP 連接配接的兩端都設有發送緩存和接受緩存,用來臨時存放雙向通信的資料全雙工通信
-
。TCP 中的“流”指的是面向位元組流
流入到程序或從程序流出的位元組序列
PHP面試問答:
https://github.com/colinlet/PHP-Interview-QA?utm=alc包含網絡協定、資料結構與算法、PHP、Web、MySQL、Redis、Linux、安全、設計模式、架構、自我介紹、離職原因、職業規劃、準備問題等部分
如果覺得不錯歡迎 star 關注,正在不斷持續更新中~~
面向位元組流
“面向位元組流”的含義是:雖然應用程式和 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 位址)不同
- 允許應用程式通路連網協定的
,即運輸層和應用層之間的接口,稱為 socket API,并簡稱為 socket應用程式設計接口 API(Application Programming Interface)
- 在 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=1 時,表明緊急指針字段有效。它告訴系統此封包段中有緊急資料,應盡快傳送(相當于高優先級的資料),而不是按原先的排隊順序來傳送緊急 URG(URGent)
-
僅當 ACK=1 時确認号字段才有效。當 ACK=0 時,确認号無效。TCP 規定,在連接配接建立後所有傳送的封包段都必須把 ACK 置1确認 ACK(ACKnowledgment)
-
當兩個應用程序進行互動式的通信時,有時在一端的應用程序希望在鍵入一個指令後立即就能夠收到對方的響應推送 PSH(Push)
-
當 RST=1 時,表明 TCP 連接配接中出現嚴重差錯(如由于主機崩潰或其他原因),必須釋放連接配接,然後再重建立立運輸連接配接複位 RST(ReSeT)
-
在連接配接建立時用來同步序号。當 SYN=1 而 ACK=0 時,表明這是一個連接配接請求封包段。對方若同意建立連接配接,則應在響應的封包段中使 SYN=1 和 ACK=1同步 SYN(SYNnchronization)
-
用來釋放一個連接配接。當 FIN=1 時,表明此封包段的發送發的資料已發送完畢,并要求釋放運輸連接配接終止 FIN(FINis)
-
占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 位元組時,就組裝成一個 TCP 封包段發送出去最大封包段長度 MSS
- 由發送方的應用程序指明要求發送封包段,即 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 程序都處于
狀态。在本執行個體中,ACLOSED(關閉)
,而 B主動打開連接配接
被動打開連接配接
- B 的 TCP 伺服器程序先建立
TCB,并處于傳輸控制塊
狀态,等待客戶的連接配接請求LISTEN(收聽)
- A 的 TCP 客戶程序建立
TCB。并向 B 發出連接配接請求封包段,首部中的同部位 SYN = 1,選擇一個初始序号 seq = x。TCP 用戶端程序進入傳輸控制子產品
狀态。TCP 規定,SYN 封包段(即 SYN = 1 的封包段)不能攜帶資料,但要SYN-SENT(同步已發送)
消耗一個序号
- 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 進入
狀态,等待 B 的确認。TCP 規定,FIN 封包段即使不攜帶資料,也消耗一個序号FIN-WAIT-1(終止等待1)
- B 收到連接配接釋放封包段後即發出确認,确認号是 ack = u + 1,而這個封包段自己的序号是 v,等于 B 前面已傳送過的資料的最後一個位元組的序号加1。B随即進入
狀态。TCP 伺服器程序這時應通知高層應用程序,因而從 A 到 B 這個方向的連接配接就釋放了,這時的 TCP 連接配接處于CLOSE-WAIT(關閉等待)
狀态,即 A 已經沒有資料要發送了,但 B 若發送資料,A 仍要接收。也就是說,從 B 到 A 這個方向的連接配接并未關閉,這個狀态可能會持續一段時間半關閉(half-close)
- A 收到來自 B 的确認後,就進入
狀态,等待 B 發出的連接配接釋放封包段FIN-WAIT-2(終止等待2)
- 若 B 已經沒有要向 A 發送的資料,其應用程序就通知 TCP 釋放連接配接。這時 B 發出的連接配接釋放封包段必須使 FIN = 1。現假定 B 的序号為 w(在半關閉狀态 B 可能又發送了一些資料)。B 還必須重複上次已發送過的确認号 ack = u + 1。這時 B 就進入
狀态,等待 A 的确認LAST-ACK(最後确認)
- A 在收到 B 的連接配接釋放封包段後,必須對此發出确認。在确認封包段中把 ACK 置1,确認号 ack = w + 1,而自己的序号是 seq = u + 1(根據 TCP 标準,前面發送過的 FIN 封包段要消耗一個序号)。然後進入到
狀态。此時 TCP 連接配接還沒有釋放掉。必須經過TIME-WAIT(時間等待)
設定的時間2MSL後,A 才進入到時間等待計時器(TIME-WAIT timer)
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 連接配接狀态名。狀态之間的箭頭表示可能發生的狀态變遷。箭頭旁邊的字,表明引起這種變遷的原因,或表明發生狀态變遷後又出現什麼動作。請注意圖中有三種不同的箭頭。
粗實線箭頭
表示對
客戶程序的正常變遷
粗虛線箭頭
伺服器程序的正常變遷
。另一種
細線箭頭
表示
異常變遷
《TCP協定詳解》原文連結:
https://blog.maplemark.cn/2019/04/tcp%E5%8D%8F%E8%AE%AE%E8%AF%A6%E8%A7%A3.html?utm=alc