網(wǎng)站投入費用百度知道答題賺錢
目錄
一.定義
二.TCP 協(xié)議報文格式
三.確認應(yīng)答(ACK)機制
四.捎帶應(yīng)答
五.連接管理機制?
六.滑動窗口
七.快重傳
八.擁塞控制
九.延時應(yīng)答
十.面向字節(jié)流
十一.粘包問題
十二.異常情況
十三.TCP 小結(jié)
一.定義
傳輸控制協(xié)議(TCP,Transmission Control Protocol)是一種面向連接的、可靠的、基于字節(jié)流的傳輸層通信協(xié)議,由IETF的RFC?793?[1]定義。
TCP旨在適應(yīng)支持多網(wǎng)絡(luò)應(yīng)用的分層協(xié)議層次結(jié)構(gòu)。 連接到不同但互連的計算機通信網(wǎng)絡(luò)的主計算機中的成對進程之間依靠TCP提供可靠的通信服務(wù)。TCP假設(shè)它可以從較低級別的協(xié)議獲得簡單的,可能不可靠的數(shù)據(jù)報服務(wù)。 原則上,TCP應(yīng)該能夠在從硬線連接到分組交換或電路交換網(wǎng)絡(luò)的各種通信系統(tǒng)之上操作。
我們主要理解以下TCP協(xié)議四個特點:
- ?傳輸層協(xié)議
- ?有連接
- ?可靠傳輸
- ?面向字節(jié)?
通過下面的講解,你會明白為什么TCP協(xié)議具有上述特點。?
二.TCP 協(xié)議報文格式
報文結(jié)構(gòu)如下:
?
?總體分為倆部分:正文(即上圖中數(shù)據(jù))+協(xié)議報頭(除了數(shù)據(jù)的其他內(nèi)容)。
下面我們先對部分內(nèi)容進行講解:
1.? 源/目的端口號: 表示數(shù)據(jù)是從哪個進程來, 到哪個進程去。
2.? 4 位 TCP 報頭長度: 表示該 TCP 頭部有多少個 32 位 bit(有多少個 4 字節(jié)); 所以 TCP 頭部最大長度是 15(2的5次方-1) * 4 = 60?字節(jié),即選項內(nèi)容最多40字節(jié)。
3.??6 位標(biāo)志位(后面詳細講解):
- ?????????URG: 緊急指針是否有效
- ? ? ????ACK: 確認號是否有效
- ????????PSH: 提示接收端應(yīng)用程序立刻從 TCP 緩沖區(qū)把數(shù)據(jù)讀走
- ????????RST: 對方要求重新建立連接; 我們把攜帶 RST 標(biāo)識的稱為復(fù)位報文?
- ?????????SYN: 請求建立連接,我們把攜帶 SYN 標(biāo)識的稱為同步報文段
- ?????????FIN: 通知對方, 本端要關(guān)閉了, 我們稱攜帶 FIN 標(biāo)識的為結(jié)束報文????????
4.? 16 位校驗和: 發(fā)送端填充, CRC 校驗. 接收端校驗不通過, 則認為數(shù)據(jù)有問題. 此處的檢驗和不光包含 TCP 首部, 也包含TCP數(shù)據(jù)部?。
5.? 16 位緊急指針: 標(biāo)識哪部分數(shù)據(jù)是緊急數(shù)據(jù)。當(dāng)緊急指針置為1,操作系統(tǒng)會優(yōu)先讀取該報文,再讀取緊急指針找到緊急數(shù)據(jù)。
6.? 16位窗口大小,表示當(dāng)前接受數(shù)據(jù)的剩余空間。
其他協(xié)議內(nèi)容后面講解。
三.確認應(yīng)答(ACK)機制
?????????當(dāng)客戶端傳輸報文時,我們應(yīng)該如何保證報文被服務(wù)端收到呢?讓服務(wù)端進行回應(yīng)。打個比方,當(dāng)我們的輔導(dǎo)員在班群里發(fā)送重要通知時,總是會跟上一句收到請回復(fù),當(dāng)所有人回復(fù)收到時,就能保證信息被所有人收到。確認應(yīng)答機制就是這個原理,簡單了解就是收到請回復(fù)?。
? ? ? ? 在這里我們就能明白32位序號和32位確認序號的作用。32位序號,用來標(biāo)識發(fā)送報文的序號,確認序號則是在收到報文為了進行應(yīng)答,將收到32號序號+1組成確認序號發(fā)生給對方進行應(yīng)答。即32位確認序號=收到的32報文序號+1,用來表示確認序號之前的報文都收到了。
過程如下?:
? ? ? ? 這里我們可能會有疑問:為什么需要倆個序號?只要有一個32位序號,當(dāng)收到報文后,回復(fù)時用這一個32序號+1發(fā)生給對方,也能進行應(yīng)答。需要倆個32位序號的原因是什么?這就是下面的主題。
四.捎帶應(yīng)答
? ? ? ? 當(dāng)客戶端給服務(wù)端發(fā)送報文后,服務(wù)端需要對發(fā)送的報文進行應(yīng)答,如果在同時服務(wù)端也需要發(fā)送報文,那么應(yīng)答和報文就會一起發(fā)送,稱為捎帶應(yīng)答。舉個例子:你和室友聊天,你問室友,你吃了嗎?室友回復(fù):吃了,你作業(yè)寫了嗎?這段對話中,室友的回復(fù)包含了應(yīng)答,同時也傳輸了信息,就是捎帶應(yīng)答。
? ? ? ? 這里倆個32位序號就很有必要了,32位確認序號表示應(yīng)答的,32位序號表示傳輸信息的。
五.連接管理機制?
在正常情況下, TCP 要經(jīng)過三次握手建立連接, 四次揮手斷開連接。我們一一講解。
我們以客戶端主動發(fā)起連接,主動斷開連接為例子。
三次握手時:? ? ? ?
服務(wù)端狀態(tài)轉(zhuǎn)化:
- [CLOSED -> LISTEN] 服務(wù)器端調(diào)用 listen 后進入 LISTEN 狀態(tài), 等待客戶端連 接;
- [LISTEN -> SYN_RCVD] 一旦監(jiān)聽到連接請求(同步報文段), 就將該連接放入內(nèi)核等待隊列中, 并向客戶端發(fā)送 SYN 確認報文.
- [SYN_RCVD -> ESTABLISHED] 服務(wù)端一旦收到客戶端的確認報文, 就進入 ESTABLISHED 狀態(tài), 可以進行讀寫數(shù)據(jù)了.
客戶端狀態(tài)轉(zhuǎn)化:
- [CLOSED -> SYN_SENT] 客戶端調(diào)用 connect, 發(fā)送同步報文段;
- [SYN_SENT -> ESTABLISHED] connect 調(diào)用成功, 則進入 ESTABLISHED 狀 態(tài), 開始讀寫數(shù)據(jù)
四次揮手時:
服務(wù)端狀態(tài)轉(zhuǎn)化:
- ?[ESTABLISHED -> CLOSE_WAIT] 當(dāng)客戶端主動關(guān)閉連接(調(diào)用 close), 服務(wù)器會收到結(jié)束報文段, 服務(wù)器返回確認報文段并進入 CLOSE_WAIT;
- [CLOSE_WAIT -> LAST_ACK] 進入 CLOSE_WAIT 后說明服務(wù)器準(zhǔn)備關(guān)閉連接(需要處理完之前的數(shù)據(jù)); 當(dāng)服務(wù)器真正調(diào)用 close 關(guān)閉連接時, 會向客戶端發(fā)送 FIN, 此時服務(wù)器進入 LAST_ACK 狀態(tài), 等待最后一個 ACK 到來(這個 ACK 是客戶 端確認收到了 FIN)
[LAST_ACK -> CLOSED] 服務(wù)器收到了對 FIN 的 ACK, 徹底關(guān)閉連接。
客戶端狀態(tài)轉(zhuǎn)化:
- [ESTABLISHED -> FIN_WAIT_1] 客戶端主動調(diào)用 close 時, 向服務(wù)器發(fā)送結(jié) 束報文段, 同時進入 FIN_WAIT_1;
- [FIN_WAIT_1 -> FIN_WAIT_2] 客戶端收到服務(wù)器對結(jié)束報文段的確認, 則進 入 FIN_WAIT_2, 開始等待服務(wù)器的結(jié)束報文段;
- [FIN_WAIT_2 -> TIME_WAIT] 客戶端收到服務(wù)器發(fā)來的結(jié)束報文段, 進入 TIME_WAIT, 并發(fā)出 LAST_ACK;
- [TIME_WAIT -> CLOSED] 客戶端要等待一個 2MSL(Max Segment Life, 報文 最大生存時間)的時間, 才會進入 CLOSED 狀態(tài)。?
這里我們主要理解?TIME_WAIT狀態(tài),主動發(fā)起斷開連接的一端需要經(jīng)歷TIME_WAIT狀態(tài)。TIME_WAIT狀態(tài)主要是為了等待一些在網(wǎng)絡(luò)中或者其他地方已經(jīng)發(fā)送但是還沒有到達的數(shù)據(jù)。如果不進行等待,當(dāng)重新進行連接時,這些數(shù)據(jù)到達會引起混亂。
三次握手原因:
- 雙方通過兩次SYN報文的發(fā)送,可以保證全雙工通信信道的暢通,同時雙方會交換彼此的窗口大小(即接收緩沖區(qū)剩余空間)和滑動窗口的頭指針位置(后面講)
- 三次握手中的最后一次ACK應(yīng)答的可靠性是無法保證的,如果服務(wù)端沒有收到客戶端的ACK應(yīng)答,則服務(wù)端內(nèi)核將不會浪費資源構(gòu)建連接相關(guān)的結(jié)構(gòu)體,但是客戶端在發(fā)送ACK應(yīng)答后(無論ACK應(yīng)答是否被對方收到),便默認連接建立成功,這種設(shè)計的意義在于連接建立失敗的資源消耗成本由客戶端來承擔(dān),從而減小服務(wù)器的負擔(dān)
- 雙方協(xié)商滑動窗口的頭指針起始位置時,會對滑動窗口的頭指針起始位置進行隨機數(shù)映射處理,防止本次通信受到歷史通信殘留在網(wǎng)絡(luò)中的報文的干擾
理解四次揮手
- 雙方斷開連接之前,必須要確認彼此都沒有數(shù)據(jù)要發(fā)送給對端,這是雙方彼此主動揮手的一個原因
- 主動斷開連接的一端,在完成第四次揮手之后,會進入TIME_WAIT狀態(tài),等待時間一般為TCP報文在網(wǎng)絡(luò)中最大存在時長的兩倍,這樣設(shè)計的主要目的是為了等待網(wǎng)絡(luò)中殘存的本次連接的通信報文消散,防止殘存報文影響下次連接通信,同時如果第四次揮手的ACK應(yīng)答丟包,服務(wù)器有足夠長的時間重新進行第三次揮手,保證連接正常關(guān)閉。
六.滑動窗口
剛才我們討論了確認應(yīng)答策略, 對每一個發(fā)送的數(shù)據(jù)段, 都要給一個 ACK 確認應(yīng)答. 收到 ACK 后再發(fā)送下一個數(shù)據(jù)段。這樣做有一個比較大的缺點, 就是性能較差. 尤其是數(shù)據(jù)往返的時間較長的時候。那么我們一次發(fā)送多條數(shù)據(jù), 就可以大大的提高性能(其實是將多個段的等待時間重疊在一起了)這就是滑動窗口。
?滑動窗口可以將數(shù)據(jù)分為三部分,已發(fā)送且收到確認的(左邊),滑動窗口中的,未發(fā)送的數(shù)據(jù)(右邊)
- 滑動窗口大小指的是無需等待確認應(yīng)答而可以繼續(xù)發(fā)送數(shù)據(jù)的最大值. 上圖的窗口大小就是 4000 個字節(jié)(四個段).在不考慮網(wǎng)絡(luò)的情況下就是對方報頭中的16位窗口大小
- ?發(fā)送前四個段的時候, 不需要等待任何 ACK, 直接發(fā)送;
- 收到第一個 ACK 后, 滑動窗口向后移動, 繼續(xù)發(fā)送第五個段的數(shù)據(jù); 依次類推;
- 操作系統(tǒng)內(nèi)核為了維護這個滑動窗口, 需要開辟 發(fā)送緩沖區(qū) 來記錄當(dāng)前還有哪 些數(shù)據(jù)沒有應(yīng)答; 只有確認應(yīng)答過的數(shù)據(jù), 才能從緩沖區(qū)刪掉;
- 窗口越大, 則網(wǎng)絡(luò)的吞吐率就越高;?
TCP報頭中的窗口大小字段用于告知對端己方接收緩沖區(qū)剩余空間,同時決定對端單次批量發(fā)送的報文數(shù)量從而決定對端的滑動窗口大小,即?
- 對端滑動窗口的尾指針 = 對端滑動窗口的頭指針 + 己方窗口大小(接收緩沖區(qū)剩余空間).?
這樣就實現(xiàn)了TCP 協(xié)議中的流量控制.達到接受和發(fā)送的平衡.?
七.快重傳
????????快重傳是基于滑動窗口一種確保TCP協(xié)議可靠性的保證,在客戶端發(fā)送消息時可能發(fā)送的消息丟失,也可能是服務(wù)端傳送的應(yīng)答丟失,這時除了超時重傳,還有一種新的重傳機制——快重傳。
????????首先,我們要明白客戶端發(fā)送的32位序號,就是發(fā)送緩存區(qū)要發(fā)送的字節(jié)最大的序號,如下面滑動窗口第一段的消息的32位序號就是2001,回應(yīng)的報文中32位確認序號就是2002,表示2002之前的字節(jié)都收到了,這時滑動窗口會向右滑動到2002。????????
?????????假設(shè),客戶端在發(fā)送消息時,第一段1001——2001的消息丟失,其他三段正常發(fā)送,那么由于服務(wù)器沒有收到第一段消息,注意32位確認序號是要表示之前的字節(jié)都收到了,服務(wù)器沒有收到第一段消息,應(yīng)答的32位確認序號只能是1002,這時客戶端就會收到多個確認序號位1002的報文,這時客戶端就會明白第一段報文有問題,會啟動快重傳,重新發(fā)送第一段報文。
? ? ? ? 如果丟失的第二端呢?這時第一段報文的應(yīng)答是正常的,滑動窗口滑動,第二段變成第一段,情況就和上面一樣了。
? ? ? ? 應(yīng)答丟失情況類似,如果第一段應(yīng)答丟失,由于客戶端依舊收到了所有消息,第二端應(yīng)答的32位序號依舊是3002,這時滑動窗口依舊會移動到3002,只是一次性移動。因此滑動窗口也保證了不需要每一個應(yīng)答都需要收到。如果后面的應(yīng)答丟失,滑動窗口滑動,情況又一樣了,后面的應(yīng)答就變成了第一段應(yīng)答。
? ? ? ? 這里最重要的就是注意32位確認序號是要表示之前的字節(jié)都收到了,理解了這個就能全部理解。
八.擁塞控制
? ? ? ? 在發(fā)送數(shù)據(jù)除了要考慮發(fā)送方和接受方的緩存區(qū)情況,即做到流量控制,我們還需要考慮網(wǎng)絡(luò)情況做到擁塞控制。
????????TCP引入慢啟動機制, 先發(fā)少量的數(shù)據(jù), 探探路, 摸清當(dāng)前的網(wǎng)絡(luò)擁堵狀態(tài), 再決定按照多大的速度傳輸數(shù)據(jù)。
- 此處引入一個概念稱為擁塞窗口
- 發(fā)送開始的時候, 定義擁塞窗口大小為 1;
- 每次收到一個 ACK 應(yīng)答, 擁塞窗口加 1;
- 每次發(fā)送數(shù)據(jù)包的時候, 將擁塞窗口和接收端主機反饋的窗口大小做比較, 取較小的值作為實際發(fā)送的窗口;即:
網(wǎng)絡(luò)阻塞情形下滑動窗口的大小
?=?min(擁塞窗口,ACK窗口大小)
- 當(dāng) TCP 開始啟動的時候, 慢啟動閾值等于窗口最大值;
- 在每次超時重發(fā)的時候, 慢啟動閾值會變成原來的一半, 同時擁塞窗口置回 1; 少量的丟包, 我們僅僅是觸發(fā)超時重傳; 大量的丟包, 我們就認為網(wǎng)絡(luò)擁塞; 當(dāng) TCP 通信開始后, 網(wǎng)絡(luò)吞吐量會逐漸上升; 隨著網(wǎng)絡(luò)發(fā)生擁堵, 吞吐量會立刻下降;
擁塞控制, 歸根結(jié)底是 TCP 協(xié)議想盡可能快的把數(shù)據(jù)傳輸給對方, 但是又要避免給網(wǎng)絡(luò) 造成太大壓力的折中方案。
九.延時應(yīng)答
如果接收數(shù)據(jù)的主機立刻返回 ACK 應(yīng)答, 這時候返回的窗口可能比較小.
- 假設(shè)接收端緩沖區(qū)為 1M. 一次收到了 500K 的數(shù)據(jù); 如果立刻應(yīng)答, 返回的窗口 就是 500K;
- 但實際上可能處理端處理的速度很快, 10ms 之內(nèi)就把 500K 數(shù)據(jù)從緩沖區(qū)消費 掉了;
- 在這種情況下, 接收端處理還遠沒有達到自己的極限, 即使窗口再放大一些, 也 能處理過來;
- ?如果接收端稍微等一會再應(yīng)答, 比如等待 200ms 再應(yīng)答, 那么這個時候返回的 窗口大小就是 1M;
一定要記得, 窗口越大, 網(wǎng)絡(luò)吞吐量就越大, 傳輸效率就越高. 我們的目標(biāo)是在保證網(wǎng)絡(luò) 不擁塞的情況下盡量提高傳輸效率; 那么所有的包都可以延遲應(yīng)答么? 肯定也不是?。
- 數(shù)量限制: 每隔 N 個包就應(yīng)答一次;
- 時間限制: 超過最大延遲時間就應(yīng)答一次;
具體的數(shù)量和超時時間, 依操作系統(tǒng)不同也有差異; 一般 N取2,超時時間取200ms。
十.面向字節(jié)流
創(chuàng)建一個 TCP 的 socket, 同時在內(nèi)核中創(chuàng)建一個 發(fā)送緩沖區(qū)和一個接收緩沖區(qū);
- ?調(diào)用 write 時, 數(shù)據(jù)會先寫入發(fā)送緩沖區(qū)中;
- ?如果發(fā)送的字節(jié)數(shù)太長, 會被拆分成多個 TCP 的數(shù)據(jù)包發(fā)出;
- ?如果發(fā)送的字節(jié)數(shù)太短, 就會先在緩沖區(qū)里等待, 等到緩沖區(qū)長度差不多了, 或 者其他合適的時機發(fā)送出去;
- ?接收數(shù)據(jù)的時候, 數(shù)據(jù)也是從網(wǎng)卡驅(qū)動程序到達內(nèi)核的接收緩沖區(qū);
- ?然后應(yīng)用程序可以調(diào)用 read 從接收緩沖區(qū)拿數(shù)據(jù);
- ?另一方面, TCP 的一個連接, 既有發(fā)送緩沖區(qū), 也有接收緩沖區(qū), 那么對于這一 個連接, 既可以讀數(shù)據(jù), 也可以寫數(shù)據(jù). 這個概念叫做全雙工
由于緩沖區(qū)的存在, TCP 程序的讀和寫不需要一一匹配, 例如:
- ?寫 100 個字節(jié)數(shù)據(jù)時, 可以調(diào)用一次 write 寫 100 個字節(jié), 也可以調(diào)用 100 次 write, 每次寫一個字節(jié);
- ?讀 100 個字節(jié)數(shù)據(jù)時, 也完全不需要考慮寫的時候是怎么寫的, 既可以一次 read 100 個字節(jié), 也可以一次 read 一個字節(jié), 重復(fù) 100 次;?
十一.粘包問題
首先要明確, 粘包問題中的 "包" , 是指的應(yīng)用層的數(shù)據(jù)包.
- ? 在 TCP 的協(xié)議頭中, 沒有如同 UDP 一樣的 "報文長度" 這樣的字段, 但是有一 個序號這樣的字段. ? 站在傳輸層的角度, TCP 是一個一個報文過來的. 按照序號排好序放在緩沖區(qū)中.
- ? 站在應(yīng)用層的角度, 看到的只是一串連續(xù)的字節(jié)數(shù)據(jù).
- ? 那么應(yīng)用程序看到了這么一連串的字節(jié)數(shù)據(jù), 就不知道從哪個部分開始到哪個部分, 是一個完整的應(yīng)用層數(shù)據(jù)包.?
那么如何避免粘包問題呢? 歸根結(jié)底就是一句話, 明確兩個包之間的邊界.
- 對于定長的包, 保證每次都按固定大小讀取即可; 例如上面的 Request 結(jié)構(gòu), 是固定大小的, 那么就從緩沖區(qū)從頭開始按 sizeof(Request)依次讀取即可;
- 對于變長的包, 可以在包頭的位置, 約定一個包總長度的字段, 從而就知道了包的結(jié)束位置;
- 對于變長的包, 還可以在包和包之間使用明確的分隔符(應(yīng)用層協(xié)議, 是程序猿 自己來定的, 只要保證分隔符不和正文沖突即可)
主要我們是通過應(yīng)用層協(xié)議來避免TCP中的粘包問題。
十二.異常情況
- 進程終止: 進程終止會釋放文件描述符, 仍然可以發(fā)送 FIN. 和正常關(guān)閉沒有什么區(qū)別.
- 機器重啟: 和進程終止的情況相同. 機器掉電/網(wǎng)線斷開: 接收端認為連接還在, 一旦接收端有寫入操作, 接收端發(fā)現(xiàn)連接已 經(jīng)不在了, 就會進行 reset. 即使沒有寫入操作, TCP 自己也內(nèi)置了一個?;疃〞r器, 會 定期詢問對方是否還在. 如果對方不在, 也會把連接釋放. 另外, 應(yīng)用層的某些協(xié)議, 也有一些這樣的檢測機制. 例如 HTTP 長連接中, 也會定期檢測對方的狀態(tài). 例如 QQ, 在 QQ 斷線之后, 也會定期嘗試重新連接.
十三.TCP 小結(jié)
為什么 TCP 這么復(fù)雜? 因為要保證可靠性, 同時又盡可能的提高性能。
可靠性: ? 校驗和 ? 序列號(按序到達) ? 確認應(yīng)答 ? 超時重發(fā) ? 連接管理 ? 流量控制 ? 擁塞控制
提高性能: ? 滑動窗口 ? 快速重傳 ? 延遲應(yīng)答 ? 捎帶應(yīng)答
其他: ? 定時器(超時重傳定時器, ?;疃〞r器, TIME_WAIT 定時器等)
基于 TCP 應(yīng)用層協(xié)議 ? HTTP ? HTTPS ? SSH ? Telnet ? FTP ? SMTP 當(dāng)然, 也包括你自己寫TCP 程序時自定義的應(yīng)用層協(xié)議。
??