TCP 可靠傳輸?shù)膶?shí)現(xiàn)(一)TCP的三次握手和四次揮手

1、介紹TCP

TCP(Transmission Control Protocol 傳輸控制協(xié)議)是一種面向連接、可靠的、基于字節(jié)流的傳輸層通信協(xié)議。

TCP將通過以下方式來提供可靠性傳輸:

TCP 可靠性傳輸實(shí)現(xiàn)(一)TCP的三次握手和四次揮手;

TCP 可靠性傳輸實(shí)現(xiàn)(二)TCP的重傳機(jī)制;

http://www.itdecent.cn/p/f7f75a0f6384

TCP 可靠性傳輸實(shí)現(xiàn)(三)TCP的流量控制和擁塞機(jī)制;

TCP 可靠性傳輸實(shí)現(xiàn)(四)TCP的?;顧C(jī)制;

TCP面向連接意味著兩個(gè)使用TCP的應(yīng)用(通常是一個(gè)客戶端和一個(gè)服務(wù)器)在彼此交換數(shù)據(jù)之前必須先建立一個(gè)TCP連接。這一過程和打電話相似,先撥號(hào)振鈴,等待對(duì)方接通后說“喂”,然后才說明是誰。在本文將介紹TCP是如何建立連接的,以及當(dāng)一方通信結(jié)束后如何斷開連接。

2、TCP的首部

TCP數(shù)據(jù)被封裝在一個(gè)IP數(shù)據(jù)報(bào)中,如圖:

TCP數(shù)據(jù)在IP數(shù)據(jù)報(bào)中的封裝

下圖顯示TCP首部的數(shù)據(jù)格式。如果不計(jì)任選字段,它通常是20個(gè)字節(jié)。

TCP包首部

每個(gè)TCP段都包含源端和目的端的端口號(hào),用于尋找發(fā)端和收端應(yīng)用程序。這兩個(gè)值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個(gè)TCP連接

一個(gè)IP地址和一個(gè)端口號(hào)也稱為一個(gè)插口(socket)。插口對(duì)(socket pair)(包含客戶IP地址、客戶端端口號(hào)、服務(wù)器IP地址和服務(wù)器端口號(hào)的四元組)可唯一確定互聯(lián)網(wǎng)絡(luò)中每個(gè)TCP連接的雙方。

下面介紹下TCP首部字段

序列號(hào):用來標(biāo)識(shí)從TCP發(fā)端向TCP收端發(fā)送的數(shù)據(jù)字節(jié)流,它表示在這個(gè)報(bào)文段中的第一個(gè)數(shù)據(jù)字節(jié)。如果把字節(jié)流看做在兩個(gè)應(yīng)用程序間的單向流動(dòng),則TCP用序號(hào)對(duì)每個(gè)字節(jié)進(jìn)行計(jì)數(shù)。序號(hào)是32bit的無符號(hào)數(shù),序號(hào)到達(dá)2^32-1后又從0開始。序號(hào)用來解決網(wǎng)絡(luò)包亂序問題。接收端根據(jù)這個(gè)編號(hào)進(jìn)行確認(rèn)

確認(rèn)號(hào):是接收確認(rèn)端期望收到的下一個(gè)序列號(hào)。確認(rèn)號(hào)應(yīng)當(dāng)是上次已成功收到的數(shù)據(jù)字節(jié)序號(hào)加1,只有當(dāng)標(biāo)志位中的ACK標(biāo)志為1時(shí)該確認(rèn)號(hào)的字段才有效。主要用來解決丟包問題。

若確認(rèn)號(hào)=N,則表明:到序號(hào)N-1為止的所有數(shù)據(jù)都已正確收到。

首部長(zhǎng)度

TCP 首部總長(zhǎng)度由該字段決定。該字段占 4bit,取最大值1111時(shí),也就是十進(jìn)制的 15,TCP 首部的偏移單位是 4 byte, 那么TCP 首部最長(zhǎng)是 15 * 4 = 60 字節(jié)。?TCP 首部總長(zhǎng)度有20個(gè)固定字節(jié),所以該字段最短是 20byte / 4byte =?5,即 0101。首部長(zhǎng)度也叫做數(shù)據(jù)偏移,因?yàn)槭撞块L(zhǎng)度實(shí)際上指示了數(shù)據(jù)區(qū)在報(bào)文段的起始偏移值。

保留位

為將來定義新的用途保留,現(xiàn)在一般置 0。

控制位:TCP首部中有6個(gè)標(biāo)志比特,它們中的多個(gè)可同時(shí)被設(shè)置為1,主要是用于操控TCP的狀態(tài)機(jī)的,依次為URG 、 ACK、? PSH、 RST、 SYN、 FIN。

\bullet ?URG:緊急指針有效,是發(fā)送端向接收端發(fā)送緊急數(shù)據(jù)的一種方式。

\bullet ?ACK:該位為1時(shí)表示【確認(rèn)應(yīng)答號(hào)】有效,TCP規(guī)定除了最初建立連接時(shí)的 SYN 號(hào)之外,該位必須設(shè)置為1。

\bullet ?PSH:接收方應(yīng)該盡快將這個(gè)報(bào)文段交給應(yīng)用層。

\bullet ?SYN:該位為1時(shí)表示希望建立連接,同步序號(hào)用來發(fā)起一個(gè)連接。

\bullet ?RST:該位為1時(shí)表示TCP連接出現(xiàn)異常,必須強(qiáng)制斷開連接。

\bullet ?FIN:該位為1時(shí)表示希望斷開連接。

窗口大小

TCP的流量控制由連接的每一端通過聲明的窗口大小來提供。

校驗(yàn)和

是一個(gè)強(qiáng)制性的字段,一定是由發(fā)送端計(jì)算和存儲(chǔ),并由接收端進(jìn)行驗(yàn)證。

緊急指針

當(dāng)URG標(biāo)志置1時(shí)緊急指針才有效。緊急指針是一個(gè)正的偏移量,和序號(hào)字段中的值相加表示緊急數(shù)據(jù)最后一個(gè)字節(jié)的序號(hào)。

選項(xiàng)字段

選項(xiàng)字段的長(zhǎng)度 = TCP 首部總長(zhǎng)度 - 20 字節(jié)固定長(zhǎng)度。 由于 TCP?首部總長(zhǎng)度最大為 60字節(jié), 那么選項(xiàng)字段的長(zhǎng)度最大為 40 字節(jié);

一個(gè)選項(xiàng)字段占 4個(gè)字節(jié)


最常見的可選字段是最長(zhǎng)報(bào)文大小,又稱為MSS(Maximum Segment Size)。每個(gè)連接方通常都在通信的第一個(gè)報(bào)文段(為建立連接而設(shè)置SYN標(biāo)志的那個(gè)段)中指明這個(gè)選項(xiàng)。它表明本端所能接收的最大長(zhǎng)度的報(bào)文段。

數(shù)據(jù)

數(shù)據(jù)部分是可選的,在連接建立和終止時(shí),雙方交換的報(bào)文段僅有TCP首部,如果一方?jīng)]有數(shù)據(jù)要發(fā)送,也使用沒有任何數(shù)據(jù)的首部來確認(rèn)收到的數(shù)據(jù)。

3、TCP連接的建立

TCP連接的建立與關(guān)閉

第一次握手:客戶端發(fā)送請(qǐng)求報(bào)文將 SYN = J 的初始序列號(hào)發(fā)送給服務(wù)端,發(fā)送完之后客戶端處于SYN_SENT狀態(tài)。

第二次握手:服務(wù)端收到SYN請(qǐng)求報(bào)文后,如果同意連接,會(huì)以自己的SYN(服務(wù)端) = K? 的初始序列號(hào)和 ack = SYN(客戶端) + 1 報(bào)文作為應(yīng)答,服務(wù)端處于SYN_RECEIVE狀態(tài)。

第三次握手:客戶端接收到服務(wù)端的SYN+ack, 發(fā)送 ack = SYN(服務(wù)端) + 1確認(rèn)包作為應(yīng)答,客戶端轉(zhuǎn)為ESTABLISHED狀態(tài)。

為什么是三次握手?不是兩次、四次?

這個(gè)問題需要回到TCP的概念上:

TCP連接是為了得到 保證連接可靠性和流量控制所需維護(hù)的某些狀態(tài)信息,這些信息的組合包括 Socket、序列號(hào)、窗口大小。 所以問題就是,為什么三次握手才可以初始化Socket、序列號(hào)、窗口大小并建立TCP連接。原因有三:

客戶端連續(xù)發(fā)送多次SYN建立連接的報(bào)文,在網(wǎng)絡(luò)擁堵等情況下出現(xiàn):

\bullet ?一個(gè)【舊的SYN報(bào)文】比【最新的SYN】報(bào)文更早到了服務(wù)端,那么此時(shí),服務(wù)端就會(huì)回一個(gè)SYN + ACK 報(bào)文給客戶端;

\bullet ?客戶端收到后根據(jù)自身的上下文,判斷這是一個(gè)歷史連接(序列號(hào)過期或超時(shí)),那么客戶端就會(huì)發(fā)送 RST 報(bào)文給服務(wù)端,表示終止這一次的連接;

如果是兩次握手連接,就不能判斷此次連接是否是歷史連接,三次握手可以讓客戶端準(zhǔn)備發(fā)送第三次報(bào)文時(shí),客戶端有足夠的上下文來判斷當(dāng)前的連接是否是歷史連接。

(2)三次握手才可以同步雙方的初始序列號(hào);

TCP的通信雙方都必須維護(hù)一個(gè)【序列號(hào)】,序列號(hào)是可靠傳輸?shù)囊粋€(gè)關(guān)鍵因素,它的作用:

\bullet ?接收方可以丟棄重復(fù)的數(shù)據(jù);

\bullet ?接收方可以根據(jù)數(shù)據(jù)包的序列號(hào)按序接收;

\bullet ?發(fā)送方可以標(biāo)識(shí)哪些數(shù)據(jù)包是已被對(duì)方接收到的;

因此在三次握手中,當(dāng)客戶端方法攜帶【初始序列號(hào)】的 SYN 報(bào)文的時(shí)候,需要服務(wù)端回一個(gè) ACK 應(yīng)答報(bào)文,表示客戶端的 SYN報(bào)文已被服務(wù)端成功接收, 那當(dāng)服務(wù)端發(fā)送【初始序列號(hào)】給客戶端的時(shí)候,也需要得到客戶端的應(yīng)答回應(yīng),這樣才能保證雙方的初始序列號(hào)能被可靠的同步。

四次握手是把 服務(wù)端回應(yīng)客戶端的 ACK 請(qǐng)求和服務(wù)端發(fā)送給服務(wù)端的初始序列號(hào)(第二步、第三步)合并成了一步,因此就成了【三次握手】。

(3)三次握手才可以避免資源的浪費(fèi);

如果只有【兩次握手】,當(dāng)客戶端的 SYN 請(qǐng)求連接在網(wǎng)絡(luò)中阻塞,客戶端超時(shí)沒有收到 ACK 應(yīng)答報(bào)文就會(huì)重新發(fā)送 SYN,由于沒有三次握手,服務(wù)端直接就分配資源并建立連接會(huì)導(dǎo)致:

建立了多個(gè)冗余的無效連接,造成不必要的浪費(fèi)。

兩次握手會(huì)導(dǎo)致資源浪費(fèi)

4、TCP連接的斷開

四次揮手

第一次揮手:首先進(jìn)行關(guān)閉的一方(即發(fā)送第一個(gè) FIN)將執(zhí)行主動(dòng)關(guān)閉,而另一方(收到這個(gè) FIN)執(zhí)行被動(dòng)關(guān)閉??蛻舳税l(fā)送完 FIN 報(bào)文后,就進(jìn)入了 FIN_WAIT_1 狀態(tài);

第二次揮手:服務(wù)器收到這個(gè) FIN ,它發(fā)回一個(gè)確認(rèn)應(yīng)答 ACK,ack = M + 1 (即,收到的序號(hào)+1)? 表示收到了,接著服務(wù)端進(jìn)入CLOSE_WAIT 狀態(tài)??蛻舳耸盏椒?wù)端的 ACK 應(yīng)答報(bào)文后,進(jìn)入 FIN_WAIT_2 狀態(tài)。

第三次揮手:等待服務(wù)端處理完數(shù)據(jù)后,也向客戶端發(fā)送 FIN 報(bào)文,之后服務(wù)端進(jìn)入 LAST_ACK 狀態(tài)。

第四次揮手:服務(wù)端收到了 ACK 應(yīng)答報(bào)文后,就進(jìn)入了 CLOSE 狀態(tài),至此服務(wù)端完成了連接的關(guān)閉。

為什么揮手需要四次?

\bullet ?關(guān)閉連接時(shí),客戶端向服務(wù)端發(fā)送 FIN 時(shí),僅僅表示客戶端不再發(fā)送數(shù)據(jù)了,但還是能接收數(shù)據(jù)。

\bullet ?服務(wù)端收到客戶端的 FIN 報(bào)文時(shí),先回一個(gè) ACK 應(yīng)答報(bào)文,而服務(wù)端可能還有數(shù)據(jù)需要處理和發(fā)送,等服務(wù)端不再發(fā)送數(shù)據(jù)時(shí),才發(fā)送 FIN 報(bào)文給客戶端表示同意關(guān)閉連接。

服務(wù)端的 ACK 和 FIN 一般都會(huì)分開發(fā)送,從而比三次握手多了一次。

5、Socket編程

應(yīng)用在使用 TCP 或 UDP 時(shí),會(huì)用到操作系統(tǒng)提供的類庫。這種類庫一般被稱為API(Application Programming Interface 應(yīng)用編程接口),使用 TCP 或 UDP 通信時(shí),又會(huì)廣泛使用到套接字(Socket)的API。

Socket

那么TCP如何利用Socket編程

基于TCP的Socket通信

\bullet ?服務(wù)端和客戶端初始化 socket,得到文件描述符;

\bullet ?服務(wù)端調(diào)用 bind ,將綁定 IP 地址和端口號(hào);

\bullet ?服務(wù)端調(diào)用 listen ,進(jìn)行監(jiān)聽;

\bullet ?服務(wù)端調(diào)用 accept,等待客戶端連接;

\bullet ?客戶端調(diào)用 connect,向服務(wù)端的地址和端口發(fā)起連接請(qǐng)求;

\bullet ?服務(wù)端 accept 返回用于傳輸?shù)?socket 的文件描述符;

\bullet ?客戶端調(diào)用 write 寫入數(shù)據(jù); 服務(wù)端調(diào)用 read 讀取數(shù)據(jù);

\bullet ?客戶端斷開連接時(shí),會(huì)調(diào)用 close , 服務(wù)端 read 讀取數(shù)據(jù)的時(shí)候,就會(huì)讀取到 EOF,待處理完數(shù)據(jù)后,服務(wù)端調(diào)用 close 表示連接關(guān)閉。

注意:服務(wù)端調(diào)用 accept 時(shí),連接成功了會(huì)返回一個(gè)已完成連接的socket,后續(xù)用來傳輸數(shù)據(jù)。所以,監(jiān)聽的 socket 和 用來傳輸數(shù)據(jù)的 socket,是兩個(gè) socket,一個(gè)是 監(jiān)聽 socket,一個(gè)是 已完成連接 socket。

重點(diǎn)講下三次握手連接請(qǐng)求的實(shí)現(xiàn):

通過隊(duì)列實(shí)現(xiàn)

處于 listen 狀態(tài)的 TCP socket,有兩個(gè)獨(dú)立的隊(duì)列:

\bullet ?SYN 隊(duì)列(SYN Queue)

存儲(chǔ)了收到了 SYN 包的連接,它的職責(zé)是回復(fù) SYN + ACK 包,并且在客戶端沒有收到 ACK包時(shí)執(zhí)行重傳。 發(fā)送完 SYN + ACK 之后, SYN 隊(duì)列等待從客戶端發(fā)出的 Ack 包(三次握手的最后一個(gè)包)。當(dāng)收到 ACK 包時(shí),首先找到對(duì)應(yīng)的 SYN 隊(duì)列,再在對(duì)應(yīng)的 SYN隊(duì)列中檢查相關(guān)的數(shù)據(jù)是否匹配,如果匹配,則將連接相關(guān)的數(shù)據(jù)從 SYN 隊(duì)列中移除,創(chuàng)建一個(gè)完整的連接(用于傳輸數(shù)據(jù)的),并將這個(gè)連接加入到 Accept 隊(duì)列。

\bullet ?Accept 隊(duì)列(Accept Queue)

存放的是已建立好的連接,等待被應(yīng)用程序取走。 當(dāng)進(jìn)程調(diào)用 accept() 時(shí), 將 socket 從隊(duì)列中取出,傳遞給上層應(yīng)用程序。


參考文檔:
1、TCP/IP 詳解

2、圖解tcpip

3、https://mp.weixin.qq.com/s/tH8RFmjrveOmgLvk9hmrkw

4、https://mp.weixin.qq.com/s/5VXhL0dTFcWNyfQ7-7NBEg

5、https://mp.weixin.qq.com/s?src=11&timestamp=1592656050&ver=2412&signature=8YnNG5TR2QrxJK4CPHAknP8I4ujYG2voPwedWyH1EJXVSQUKDS5IaHvbMd3S7kpL7GrCaZkQbvgbh5OoX9IZ777abi2e3Ze-Cu1DCoLk9cexKOng3oXRfgt60fOU2gNF&new=1

6、https://blog.csdn.net/mary19920410/article/details/58030147

7、https://blog.csdn.net/Mary19920410/article/details/72857764

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容