1. 程式人生 > >TPC協議學習總結(上)

TPC協議學習總結(上)

組成 三次 -s 序列號 可謂 然而 image 計數器 結構

在計算機領域,數據的本質無非0和1,創造0和1的固然偉大,但真正百花齊放的還是基於0和1之上的各種層次之間的組合(數據結構)所帶給我們人類各種各樣的可能性。例如TCP協議,我們的生活無不無時無刻的站在TCP協議這個“巨人”的肩膀上,最簡單的一個打開手機的動作。所以對TCP的認識和理解,可謂越來越常識化。

技術分享圖片

TCP/IP五層協議

雖然TCP是一種計算機網絡協議,但本質還是人與人之間的一種約定,只不過由計算機去執行而已,把協議的細節與作用解耦,讓我們人類只需專註於基於它的應用呈現之上即可。協議即“規則”,如果我們把光纖“橫斜面”剖析,我們看到的就是數據的本質0和1,如下圖所示:

技術分享圖片

0和1是點對點之間通信的信息“載體”,我們需要有一各規則去翻譯這些“載體”,好比如小白和小黑之間的“敲聲傳話遊戲”的約定,他們可以約定“敲一下”代表“是”,“敲兩下”代表“不是”等。這些“敲聲”跟光纖上的“0”和“1”都是承載著一樣的任務——信息載體。

技術分享圖片

從整個網絡層次來看,TCP/IP協議體系是網絡的一個核心協議組,有一點需要知道的是TCP/IP協議體系並非只有TCP協議和IP協議,而是包含了物理層、鏈路層、網絡層、運輸層、應用層,而每一層次又有不同的協議,例如運輸層協議除了TCP協議還有UDP協議。當然這裏我只是為了接下來學習TCP協議的一個宏觀認識。從上圖可以看出,從0和1的基本信息單元到TCP協議的數據結構還要經過鏈路層和網絡層的層層分解,換句話說,也就是TCP協議的數據以“段”單元,封裝在鏈路層的IP協議上,IP協議的數據是以“數據報”為單元,它同樣封裝在鏈路層的以太網標準協議裏面。本文的重點在TCP協議的學習,了解了TCP的原理,其他協議的數據結構和邏輯大同小異了。

TCP的首部

技術分享圖片

從“TCP/IP五層協議體系圖”可以看出,每一個協議都會有個“頭部”,TCP也不例外,其實這個“頭部”就是該協議的數據結構以及規則的說明,但無論協議的玩法如何變化,它還是離不開0和1的信息載體。

源端口號:我們都知道IP是跟主機相關,而每臺主機又可以有不同的應用進程在運行,所以端口更多可以指運行在主機上的應用進程,所以源端口號也就是基於TCP協議傳輸數據的“發送方”。

目的端口:就是等待TCP協議發送方數據的“接收方”,其實所謂的端口也就是應用進程與應用進程之間通信的監聽出入口。

序列號:這個數字是用來表示通信雙方“單向”數據量流動數量表示,上面所介紹的0和1是最小的數據傳輸單元,我們稱為“比特(bit)”。而這個序列號記錄的是以“字節”為單位的計數器(1字節=8比特)。例如A要傳輸給B的512字節數據,假設初始序列號為1024(註意:每次初始化序號都會不一樣,TCP有一個比較復雜的初始化算法),那麽他們傳輸過程的序列號為1536。這個序列號會隨著雙方“交流”而不斷的增加,因為序列號一共32比特,所以最大值也就是2^32-1,到達最大值後重新從0開始。因為TCP是一個可靠的協議,序列號的存在是其可靠的關鍵因素之一。

技術分享圖片

確認序列號:既然每個傳輸的字節都被計數,確認序列號包含發送確認的一端所期望收到下一個序號。因此,確認序列號應當是上次已成功接收到數據字節序列號加1。只有ACK標識(下面會介紹)為1時確認序列號才生效。因為TCP為應用層提供雙工服務,意味著數據能在兩個方向上獨立地進行傳輸,因此連接的每一端(客戶端和服務端)必須保持每個方向上的傳輸序列號。例如A傳送給B的序列號為1024(A維護),但B傳送給A的有自己的序列號需要維護(B維護)。

首部長度:TCP首部的“選項”不啟用,那麽TCP的頭部就是20字節,但因為存在“選項”的部分,所以頭部可能存在大於20字節的可能性。因為“首部長度標識”有4位,所以最大值為2^4-1=15,而這個標識維護頭部的長度是以32比特為單元,所以頭部最大長度為15*32比特(4字節)=60字節。

標誌:每個標誌占1比特,它們中的多個可同時被設置為1,每個標誌的用法如下:

URG:緊急指針(urgent pointer)有效;

ACK:確認序號有效;

PSH:接收方應該盡快將這個報文段交給應用層;

RST:重建連接;

SYN:同步序號用來發起一個連接;

FIN:發送端完成發送任務;

窗口大小:TCP的流量控制由連接的每一端通過聲明的窗口大小來提供(以字節為單位),窗口大小是一個16比特字段,因而窗口最大為65535字節。換個說法,窗口好比如“緩沖區”,TCP是一個雙工單向傳送的通信協議,雙方都需要有自己的窗口(緩沖區)大小相互告知,如果接收到的應用處理速度慢(從緩沖區消費數據慢),那麽它的窗口很容易就滿了,發送方就會停止發送,等到接受方的窗口有“空余”了才繼續發送。

檢驗和:檢驗和(類數據簽名)覆蓋了整個的TCP報文段:TCP首部和TCP數據,因為TCP是一個可靠的協議,所以這是強制性的字段,由發送方計算和設置,並由接收方進行驗證,這就是可靠性保證的重要手段。

緊急指針:只有當URG標誌置1時緊急指針才有效。緊急指針是一個正的偏移量,和序號字段中的值相加表示緊急數據最後一個報文段。

選項:就是TCP頭部的不是“必須”的選項,例如常見的可選字段是“最長報文大小”,又稱為MSS(Maximun Segment Size),每個連接方通常都在通信的第一個報文段中指明這個選項。

數據:整個TCP報文段是又報文頭部和報文數據組成的,除去了頭部就是數據,但數據是可空的,例如創建連接(SYN)和結束傳輸(FIN)的TCP報文都是沒有數據的。

TCP連接的建立和終止

技術分享圖片

TCP建立連接需要三次握手,分別如下:

1)、客戶端(請求方)發送一個SYN段指明客戶打算連接的服務器端口,以及把初始化序號x附上,這就是大名鼎鼎的SYN報文段,在介紹頭部的時候已經提過,SYN報文段是沒有數據的,因為連接都沒正式連接,發送數據沒意義。但也提到了客戶端會附上它的最大報文段,也就是告訴接收方它最大的一個報文段能接受多少數據。

2)、服務端(處於監聽狀態)收到SYN請求後發回包含服務端的初始序號的SYN報文段作為應答(上文提到過客戶端和服務端的初始序號都是各自維護的)。同時,將確認序號設置為客戶的ISN加1(因為SYN將占用一個序號),以對客戶的SYN報文段進行確認。在服務端想客戶端響應SYN的時候同樣可能會附上它接收的最大報文段,但記住,畢竟最大報文段是可選的,不一定會存在,不相互告知的話就會使用默認值。

3)、客戶端必須將確認序號設置為服務器的ISN加1一對服務器的SYN報文段進行確認。

當以上三個報文段完成交互後就證明連接已經建立,這個過程也成為“三次握手”。接下來客戶端就可以發送數據給服務端,服務端可以響應數據。其實很多時候,客戶端在第三個報文段(也就是第三次握手)的時候就已經附帶數據了。因為它已經不需要等待對方第四次握手的交互確認。正常連接的第四個報文段也是客戶端發送數據的報文段,所以既然第三次和第四次都是客戶端,為了省了一個交互,客戶端可以直接從第三個報文段(應答服務端ack)附上數據。

建立一個連接需要三次握手,而終止一個連接需要經過4次握手,這是由於TCP的半關閉(half close)造成的。既然一個TCP連接是全雙工的(即數據在兩個方向上能同時傳遞),因此每個方向必須單獨地進行關閉。當一端收到一個FIN,它必須通知應用層另一端已經終止了那個方向的數據傳送。發送FIN通常是應用層進行關閉的結果。比較常見的還是客戶端關閉,但服務端也可以設置主動關閉,例如Nginx相關策略配置。

技術分享圖片

TCP終止連接需要四次握手,分別如下:

1)、首先關閉的一方(即發送第一個FIN)將執行主動關閉,上圖顯示主動關閉的一方是客戶端。

2)、當服務端收到這個FIN報文段時,它將發回一個ACK,確認序號為收到的序號加1,就像上圖的ack=u+1,因為FIN跟SYN一樣也占用一個序號。

3)、服務端把收到的FIN的消息告訴應用程序(傳送一個文件結束符),接著這個應用程序就會關閉它的連接(以上提過,建立和關閉都是由應用主動發起的),導致服務端的TCP端發送一個FIN給客戶端。需要註意的是,畢竟TCP是雙工的,客戶端關閉連接不代表服務端就可以立刻關閉,如果客戶端發起關閉的時候,服務端還沒有響應完數據給客戶端,服務端還是需要把數據發完了再去關閉的,而客戶端主動發起了閉關也不會立刻罷工,它還是會進入“FIN_WAIT2”狀態進行數據接收,直到服務端發送完了並最後發送結束連接報文段(FIN),才進入TIME_WAIT狀態。

4)、客戶端收到服務端的FIN報文段時,它會立刻對此FIN進行ACK回復,服務端收到後就直接進入關閉狀態(CLOSED)。

因為TCP是全雙工的,雙方都各種維護自己單向傳送數據的連接,所以必然會存在雙方同時主動關閉的情況,如下圖所示:

技術分享圖片

當雙方同時向對方發送FIN執行主動連接時,雙方均從ESTABLISHED狀態變為FIN_WAIT_1狀態。雙方都收到FIN後,狀態由FIN_WAIT_1變遷至CLOSING,並發送最後的ACK。當收到ACK時,雙方的狀態變為TIME_WAIT。

TCP的狀態遷變

通過以上建立和終止連接可以看到,無論客戶端還是服務端,無論是連接方還是結束方都存在許多“狀態”,每個狀態隨著各種條件不斷變化,具體狀態的遷變可以通過下圖來進行總結。

技術分享圖片

2MSL等待狀態

從上圖遷變狀態可以看到,TCP主動關閉的一方都會進入TIME_WAIT狀態,也稱為2MSL(最大報文段生存時間)等待狀態。之所以要等待,是因為關閉方要確認處於“CLOSE_WAIT”狀態的被關閉方收到它最後的ACK報文,報文的在網絡上單向傳送的最大時間叫做MSL,那麽等待確認報文來回的時間就是2MSL,如果被關閉方在2MSL內都沒有收到ACK,它會繼續發送FIN報文,而如果關閉方在2MSL內沒有收到對方的報文就默認對方已經收到。

報文在網絡上的生存時間並不只有TCP決定的,在網絡層的IP協議對數據報同樣存在著網絡單向傳送的時間限制,這個限制的約定叫TTL(Time To Live)。TTL的時間單位並非時間單位,而是“跳數”,數據包每經過一個路由就叫“一跳”,不同系統對IP數據包的跳數初始值都不一樣,例如有些Linux默認值是255。每經過一個路由,總生命跳數就減1,直到為0都還沒有到達目的地就丟棄。255跳到底是多少秒呢?其實這都是一個不確定數字。如果一個數據包經過255個路由都還沒到達目的地,我想目的地可能是“火星”。並TCP是“坐”在IP協議之上的,所以TCP的MSL肯定不能比TTL短,RFC793[Postel 1981c]指出MSL為2分鐘。然而,實現中的常用值是30秒,1分鐘或2分鐘。要知道,0和1在光纖上傳送的速度是“光速(約300000km/s)”,30秒的時間跑了不知道多少趟地球了,所以正常情況下都會大於TTL了(除非部分路由十分磨蹭)。如果做過一些高並發系統的同學,多少會遇到一些諸如time_wait過多的現象,例如WEB服務器配置主動關閉連接策略或連接有效時間短而主動關閉,大量的time_wait會占用文件描述符,而很容易導致耗光系統默認的1024個最大文件打開數(fs.file-max)而無法正常服務。

同時打開和同時關閉

有時候TCP建立連接不一定必須是三次握手,有時可能會是4次。沒錯,當雙發同時進行請求主動打開連接的時候就是4次,如下圖所示。這個時候,並沒有誰是客戶端誰是服務端之稱,因為雙方都有主動發送數據的權利。這種情況應該很少見,如果需要模擬還是可以的,把雙方的網速通過某些手段把它降低,那麽就有可能演示。

技術分享圖片

學習總結

本次總結更多是對TCP協議的一個基礎了解,包括TCP建立連接的正常三次握手和十分罕見的同步建立連接的4次握手,以及關閉連接的正常4次握手和同步關閉連接導致雙方都進入TIME_WAIT狀態的4次握手。最後總體學習了TCP客戶端以及服務端各種狀態遷變的概要圖,十分清晰地對TCP各種概況的描述,以及為什麽會有TIME_WAIT和2MSL的概念。

TPC協議學習總結(上)