1、介紹TCP
TCP(Transmission Control Protocol 傳輸控制協議)是一種面向連接、可靠的、基于字節流的傳輸層通信協議。
TCP將通過以下方式來提供可靠性傳輸:
TCP 可靠性傳輸實現(一)TCP的三次握手和四次揮手;
TCP 可靠性傳輸實現(二)TCP的重傳機制;
http://www.lxweimin.com/p/f7f75a0f6384
TCP 可靠性傳輸實現(三)TCP的流量控制和擁塞機制;
TCP 可靠性傳輸實現(四)TCP的保活機制;
TCP面向連接意味著兩個使用TCP的應用(通常是一個客戶端和一個服務器)在彼此交換數據之前必須先建立一個TCP連接。這一過程和打電話相似,先撥號振鈴,等待對方接通后說“喂”,然后才說明是誰。在本文將介紹TCP是如何建立連接的,以及當一方通信結束后如何斷開連接。
2、TCP的首部
TCP數據被封裝在一個IP數據報中,如圖:
下圖顯示TCP首部的數據格式。如果不計任選字段,它通常是20個字節。
每個TCP段都包含源端和目的端的端口號,用于尋找發端和收端應用程序。這兩個值加上IP首部中的源端IP地址和目的端IP地址唯一確定一個TCP連接。
一個IP地址和一個端口號也稱為一個插口(socket)。插口對(socket pair)(包含客戶IP地址、客戶端端口號、服務器IP地址和服務器端口號的四元組)可唯一確定互聯網絡中每個TCP連接的雙方。
下面介紹下TCP首部字段:
序列號:用來標識從TCP發端向TCP收端發送的數據字節流,它表示在這個報文段中的第一個數據字節。如果把字節流看做在兩個應用程序間的單向流動,則TCP用序號對每個字節進行計數。序號是32bit的無符號數,序號到達2^32-1后又從0開始。序號用來解決網絡包亂序問題。接收端根據這個編號進行確認。
確認號:是接收確認端期望收到的下一個序列號。確認號應當是上次已成功收到的數據字節序號加1,只有當標志位中的ACK標志為1時該確認號的字段才有效。主要用來解決丟包問題。
若確認號=N,則表明:到序號N-1為止的所有數據都已正確收到。
首部長度:
TCP 首部總長度由該字段決定。該字段占 4bit,取最大值1111時,也就是十進制的 15,TCP 首部的偏移單位是 4 byte, 那么TCP 首部最長是 15 * 4 = 60 字節。?TCP 首部總長度有20個固定字節,所以該字段最短是 20byte / 4byte =?5,即 0101。首部長度也叫做數據偏移,因為首部長度實際上指示了數據區在報文段的起始偏移值。
保留位:
為將來定義新的用途保留,現在一般置 0。
控制位:TCP首部中有6個標志比特,它們中的多個可同時被設置為1,主要是用于操控TCP的狀態機的,依次為URG 、 ACK、? PSH、 RST、 SYN、 FIN。
?URG:緊急指針有效,是發送端向接收端發送緊急數據的一種方式。
?ACK:該位為1時表示【確認應答號】有效,TCP規定除了最初建立連接時的 SYN 號之外,該位必須設置為1。
?PSH:接收方應該盡快將這個報文段交給應用層。
?SYN:該位為1時表示希望建立連接,同步序號用來發起一個連接。
?RST:該位為1時表示TCP連接出現異常,必須強制斷開連接。
?FIN:該位為1時表示希望斷開連接。
窗口大小:
TCP的流量控制由連接的每一端通過聲明的窗口大小來提供。
校驗和:
是一個強制性的字段,一定是由發送端計算和存儲,并由接收端進行驗證。
緊急指針:
當URG標志置1時緊急指針才有效。緊急指針是一個正的偏移量,和序號字段中的值相加表示緊急數據最后一個字節的序號。
選項字段:
選項字段的長度 = TCP 首部總長度 - 20 字節固定長度。 由于 TCP?首部總長度最大為 60字節, 那么選項字段的長度最大為 40 字節;
最常見的可選字段是最長報文大小,又稱為MSS(Maximum Segment Size)。每個連接方通常都在通信的第一個報文段(為建立連接而設置SYN標志的那個段)中指明這個選項。它表明本端所能接收的最大長度的報文段。
數據:
數據部分是可選的,在連接建立和終止時,雙方交換的報文段僅有TCP首部,如果一方沒有數據要發送,也使用沒有任何數據的首部來確認收到的數據。
3、TCP連接的建立
第一次握手:客戶端發送請求報文將 SYN = J 的初始序列號發送給服務端,發送完之后客戶端處于SYN_SENT狀態。
第二次握手:服務端收到SYN請求報文后,如果同意連接,會以自己的SYN(服務端) = K? 的初始序列號和 ack = SYN(客戶端) + 1 報文作為應答,服務端處于SYN_RECEIVE狀態。
第三次握手:客戶端接收到服務端的SYN+ack, 發送 ack = SYN(服務端) + 1確認包作為應答,客戶端轉為ESTABLISHED狀態。
為什么是三次握手?不是兩次、四次?
這個問題需要回到TCP的概念上:
TCP連接是為了得到 保證連接可靠性和流量控制所需維護的某些狀態信息,這些信息的組合包括 Socket、序列號、窗口大小。 所以問題就是,為什么三次握手才可以初始化Socket、序列號、窗口大小并建立TCP連接。原因有三:
客戶端連續發送多次SYN建立連接的報文,在網絡擁堵等情況下出現:
?一個【舊的SYN報文】比【最新的SYN】報文更早到了服務端,那么此時,服務端就會回一個SYN + ACK 報文給客戶端;
?客戶端收到后根據自身的上下文,判斷這是一個歷史連接(序列號過期或超時),那么客戶端就會發送 RST 報文給服務端,表示終止這一次的連接;
如果是兩次握手連接,就不能判斷此次連接是否是歷史連接,三次握手可以讓客戶端準備發送第三次報文時,客戶端有足夠的上下文來判斷當前的連接是否是歷史連接。
(2)三次握手才可以同步雙方的初始序列號;
TCP的通信雙方都必須維護一個【序列號】,序列號是可靠傳輸的一個關鍵因素,它的作用:
?接收方可以丟棄重復的數據;
?接收方可以根據數據包的序列號按序接收;
?發送方可以標識哪些數據包是已被對方接收到的;
因此在三次握手中,當客戶端方法攜帶【初始序列號】的 SYN 報文的時候,需要服務端回一個 ACK 應答報文,表示客戶端的 SYN報文已被服務端成功接收, 那當服務端發送【初始序列號】給客戶端的時候,也需要得到客戶端的應答回應,這樣才能保證雙方的初始序列號能被可靠的同步。
四次握手是把 服務端回應客戶端的 ACK 請求和服務端發送給服務端的初始序列號(第二步、第三步)合并成了一步,因此就成了【三次握手】。
(3)三次握手才可以避免資源的浪費;
如果只有【兩次握手】,當客戶端的 SYN 請求連接在網絡中阻塞,客戶端超時沒有收到 ACK 應答報文就會重新發送 SYN,由于沒有三次握手,服務端直接就分配資源并建立連接會導致:
建立了多個冗余的無效連接,造成不必要的浪費。
4、TCP連接的斷開
第一次揮手:首先進行關閉的一方(即發送第一個 FIN)將執行主動關閉,而另一方(收到這個 FIN)執行被動關閉。客戶端發送完 FIN 報文后,就進入了 FIN_WAIT_1 狀態;
第二次揮手:服務器收到這個 FIN ,它發回一個確認應答 ACK,ack = M + 1 (即,收到的序號+1)? 表示收到了,接著服務端進入CLOSE_WAIT 狀態。客戶端收到服務端的 ACK 應答報文后,進入 FIN_WAIT_2 狀態。
第三次揮手:等待服務端處理完數據后,也向客戶端發送 FIN 報文,之后服務端進入 LAST_ACK 狀態。
第四次揮手:服務端收到了 ACK 應答報文后,就進入了 CLOSE 狀態,至此服務端完成了連接的關閉。
為什么揮手需要四次?
?關閉連接時,客戶端向服務端發送 FIN 時,僅僅表示客戶端不再發送數據了,但還是能接收數據。
?服務端收到客戶端的 FIN 報文時,先回一個 ACK 應答報文,而服務端可能還有數據需要處理和發送,等服務端不再發送數據時,才發送 FIN 報文給客戶端表示同意關閉連接。
服務端的 ACK 和 FIN 一般都會分開發送,從而比三次握手多了一次。
5、Socket編程
應用在使用 TCP 或 UDP 時,會用到操作系統提供的類庫。這種類庫一般被稱為API(Application Programming Interface 應用編程接口),使用 TCP 或 UDP 通信時,又會廣泛使用到套接字(Socket)的API。
那么TCP如何利用Socket編程
?服務端和客戶端初始化 socket,得到文件描述符;
?服務端調用 bind ,將綁定 IP 地址和端口號;
?服務端調用 listen ,進行監聽;
?服務端調用 accept,等待客戶端連接;
?客戶端調用 connect,向服務端的地址和端口發起連接請求;
?服務端 accept 返回用于傳輸的 socket 的文件描述符;
?客戶端調用 write 寫入數據; 服務端調用 read 讀取數據;
?客戶端斷開連接時,會調用 close , 服務端 read 讀取數據的時候,就會讀取到 EOF,待處理完數據后,服務端調用 close 表示連接關閉。
注意:服務端調用 accept 時,連接成功了會返回一個已完成連接的socket,后續用來傳輸數據。所以,監聽的 socket 和 用來傳輸數據的 socket,是兩個 socket,一個是 監聽 socket,一個是 已完成連接 socket。
重點講下三次握手連接請求的實現:
處于 listen 狀態的 TCP socket,有兩個獨立的隊列:
?SYN 隊列(SYN Queue)
存儲了收到了 SYN 包的連接,它的職責是回復 SYN + ACK 包,并且在客戶端沒有收到 ACK包時執行重傳。 發送完 SYN + ACK 之后, SYN 隊列等待從客戶端發出的 Ack 包(三次握手的最后一個包)。當收到 ACK 包時,首先找到對應的 SYN 隊列,再在對應的 SYN隊列中檢查相關的數據是否匹配,如果匹配,則將連接相關的數據從 SYN 隊列中移除,創建一個完整的連接(用于傳輸數據的),并將這個連接加入到 Accept 隊列。
?Accept 隊列(Accept Queue)
存放的是已建立好的連接,等待被應用程序取走。 當進程調用 accept() 時, 將 socket 從隊列中取出,傳遞給上層應用程序。
參考文檔:
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×tamp=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