TCP協(xié)議的特性
- 順序問題 济欢,穩(wěn)重不亂赠堵;
- 丟包問題,承諾靠譜法褥;
- 連接維護茫叭,有始有終;
- 流量控制半等,把握分寸揍愁;
- 擁塞控制,知進知退杀饵;
TCP 包頭格式
包頭內(nèi)容如上圖所示
- 源端口號和目標端口號吗垮,數(shù)據(jù)從哪來到哪去。
- 包的序號凹髓,解決包亂序問題
- 確認序號烁登,發(fā)出去的包要有回復 要不然不知道包對方有沒有收到,如果沒有就會重新發(fā)送蔚舀,直到到達饵沧。
- 狀態(tài)位,日常分析有用的就五個字段 SYN, FIN, ACK, PSH, RST赌躺。TCP 是面向連接的 雙方要維護連接的狀態(tài)這些帶狀態(tài)的包的發(fā)送狼牺,會引起雙方的狀態(tài)變更。
- 窗口大小礼患,TCP 要做流量控制是钥,通信雙方各聲明一個窗口,標識當前能夠處理能力缅叠。
狀態(tài)位悄泥,日常分析有用的就五個字段
- SYN 表示建立連接
- FIN 表示關閉連接
- ACK 表示響應
- PSH 表示有DATA數(shù)據(jù)傳輸
- RST 表示連接重置
TCP 三次握手
TCP 的建立 我們常常稱為三次握手。
雙端狀態(tài)值變化(狀態(tài)機)
一開始肤粱,客戶端和服務端都處于 CLOSED 狀態(tài)弹囚。先是服務端主動監(jiān)聽某個端口,處于 LISTEN 狀態(tài)领曼。然后客戶端主動發(fā)起連接 SYN鸥鹉,之后處于 SYN-SENT 狀態(tài)。服務端收到發(fā)起的連接庶骄,返回 SYN毁渗,并且 ACK 客戶端的 SYN,之后處于SYN-RCVD 狀態(tài)(同步空閑字符接收到狀態(tài))客戶端收到服務端發(fā)送的 SYN 和 ACK 之后单刁,發(fā)送 ACK 的 ACK灸异,之后處于 ESTABLISHED (已建立)狀態(tài),因為它一發(fā)一收成功了。服務端收到 ACK 的 ACK 之后绎狭,處于 ESTABLISHED 狀態(tài)细溅,因為它也一發(fā)一收了。
我們也常稱為“請求 -> 應答 -> 應答之應答”的三個回合儡嘶。這個看起來簡單喇聊,其實里面還是有很多的學問,很多的細節(jié)蹦狂。為什么要三次誓篱?而不是兩次?兩個端一來一回不就可以了凯楔?為了可靠窜骄,為什么不四次?
- 當A要發(fā)起一個連接摆屯,到達了B B收到了請求包邻遏,知道了A 的存在 并且知道 A要和它建立連接 于是B就發(fā)了一個連接給A 這個應答包一入網(wǎng)絡深似海,不知道能不能到達A虐骑。這個時候B自然不能認為連接是建立好的准验。因為應答包仍然會丟,會繞彎路廷没,或者 A 已經(jīng)掛了都有可能糊饱。A 建立連接的時候,請求包重復發(fā)了幾次颠黎,有的請求包繞了一大圈又回來了另锋,B 會認為這也是一個正常的的請求的話,因此建立了連接狭归,可以想象夭坪,這個連接不會進行下去,也沒有個終結的時候唉铜,純屬單相思了台舱。因而兩次握手肯定不行律杠。
- B 發(fā)送的應答可能會發(fā)送多次潭流,但是只要一次到達 A,A 就認為連接已經(jīng)建立了柜去,因為對于 A 來講灰嫉,他的消息有去有回。A 會給 B 發(fā)送應答之應答嗓奢,而 B 也在等這個消息讼撒,才能確認連接的建立,只有等到了這個消息,對于 B 來講根盒,才算它的消息有去有回钳幅。當然 A 發(fā)給 B 的應答之應答也會丟,也會繞路炎滞,甚至 B 掛了敢艰。按理來說,還應該有個應答之應答之應答册赛,這樣下去就沒底了钠导。所以四次握手是可以的,四十次都可以森瘪,關鍵四百次也不能保證就真的可靠了牡属。只要雙方的消息都有去有回,就基本可以了扼睬。
三次握手除了雙方建立連接外逮栅,主要還是為了溝通一件事情,就是 TCP 包的序號的問題窗宇。
- A 要告訴 B证芭,我這面發(fā)起的包的序號起始是從哪個號開始的,B 同樣也要告訴 A担映,B 發(fā)起的包的序號起始是從哪個號開始的废士。
- 這個序號的起始序號是隨著時間變化的,可以看成一個 32 位的計數(shù)器蝇完,每 4ms 加一官硝,如果計算一下,如果到重復短蜕,需要 4 個多小時氢架,那個繞路的包早就死翹翹了,因為我們都知道 IP 包頭里面有個 TTL朋魔,也即生存時間岖研。
TCP 四次揮手
雙端狀態(tài)值變化(狀態(tài)機)
斷開的時候,我們可以看到警检,當 A 說“不玩了”孙援,就進入 FIN_WAIT_1 的狀態(tài),B 收到“A 不玩”的消息后扇雕,發(fā)送知道了拓售,就進入 CLOSE_WAIT 的狀態(tài)。
A 收到“B 說知道了”镶奉,就進入 FIN_WAIT_2 的狀態(tài)础淤,如果這個時候 B 直接跑路崭放,則 A 將永遠在這個狀態(tài)。TCP 協(xié)議里面并沒有對這個狀態(tài)的處理鸽凶,但是 Linux 有币砂,可以調(diào)整 tcp_fin_timeout 這個參數(shù),設置一個超時時間玻侥。
如果 B 沒有跑路道伟,發(fā)送了“B 也不玩了”的請求到達 A 時,A 發(fā)送“知道 B 也不玩了”的 ACK 后使碾,從 FIN_WAIT_2 狀態(tài)結束蜜徽,按說 A 可以跑路了,但是最后的這個 ACK 萬一 B 收不到呢票摇?則 B 會重新發(fā)一個“B 不玩了”拘鞋,這個時候 A 已經(jīng)跑路了的話,B 就再也收不到 ACK 了矢门,因而 TCP 協(xié)議要求 A 最后等待一段時間 TIME_WAIT盆色,這個時間要足夠長,長到如果 B 沒收到 ACK 的話祟剔,“B 說不玩了”會重發(fā)的隔躲,A 會重新發(fā)一個 ACK 并且足夠時間到達 B。
A 直接跑路還有一個問題是物延,A 的端口就直接空出來了宣旱,但是 B 不知道,B 原來發(fā)過的很多包很可能還在路上叛薯,如果 A 的端口被一個新的應用占用了浑吟,這個新的應用會收到上個連接中 B 發(fā)過來的包,雖然序列號是重新生成的耗溜,但是這里要上一個雙保險组力,防止產(chǎn)生混亂,因而也需要等足夠長的時間抖拴,等到原來 B 發(fā)送的所有的包都死翹翹燎字,再空出端口來。
等待的時間設為 2MSL阿宅,MSL 是 Maximum Segment Lifetime候衍,報文最大生存時間,它是任何報文在網(wǎng)絡上存在的最長時間家夺,超過這個時間報文將被丟棄脱柱。因為 TCP 報文基于是 IP 協(xié)議的,而 IP 頭中有一個 TTL 域拉馋,是 IP 數(shù)據(jù)報可以經(jīng)過的最大路由數(shù)榨为,每經(jīng)過一個處理他的路由器此值就減 1,當此值為 0 則數(shù)據(jù)報將被丟棄煌茴,同時發(fā)送 ICMP 報文通知源主機随闺。協(xié)議規(guī)定 MSL 為 2 分鐘,實際應用中常用的是 30 秒蔓腐,1 分鐘和 2 分鐘等矩乐。