TCP協議屬于網路分層中的傳輸層,傳輸層作用的就是建立埠與埠的通信,而其下一層網路層的主要作用是建立"主機到主機"的通信,所以在我們日常進行網路編程時只要確定主機和埠,就能實作程式之間的資料交流,在Unix系統中就把主機+埠,叫做"套接字"(socket),所以一般網路編程都是基于對于socket的操作來做的,
TCP協議其實是一個非常復雜的協議,做過網路編程開發的都聽過一句話‘’TCP本身是一種可靠的協議”,但正是為了保證可靠性,TCP 內部使用了如各種重傳與控制演算法,所以 TCP 是一個內部原理復雜,但是使用起來比較簡單的協議,
下面我們對TCP協議進行一個基本的介紹,本文只是站在應用的角度上闡述,相比與真正的深入還是比較淺顯的,
一、TCP協議格式
首先主要看下TCP協議的頭格式

其中各欄位的意義如下:
1、TCP源埠(Source Port):16位的源埠其中包含發送方應用程式對應的埠,源埠和源IP地址標示報文發送端的地址,
2、TCP目的埠(Destination port):16位的目的埠域定義傳輸的目的,這個埠指明報文接收計算機上的應用程式地址介面,
3、包序號(Sequence Number):32位的SN序列號標識了TCP報文中第一個byte在對應方向的傳輸中對應的位元組序號,用來記錄網路包順序,解決傳輸中的亂序、重復問題,比如發送端發送的一個TCP包凈荷(不包含TCP頭)為10byte,SN為5,則發送端接著發送的下一個資料包的時候,SN應該設定為5+10=15,通過序列號,TCP接收端可以識別出重復接收到的TCP包,從而丟棄重復包,同時對于亂序資料包也可以依靠系列號進行重排序,進而對高層提供有序的資料流,另外如果接收的包中包含SYN或FIN標志位,邏輯上也占用1個byte,應答號需加1,
4、確認號(Acknowledgement Number):32位的ACK標識了報文發送端期望接收的位元組序列,如果設定了ACK控制位,這個值表示一個準備接收的包的序列碼,注意是準備接收的包,比如當前接收端接收到一個凈荷為10byte的資料包,SN為5,則會回復一個確認收到的資料包,如果這個資料包之前的資料也都已經收到了,這個資料包中的ACK Number則設定為10+5=15,表示之前的資料都已經收到了,準備接受SN=15的資料包,
5、視窗(Advertised-Window):著名的滑動視窗(Sliding Window),用于TCP的流量控制,
6、狀態位(TCP-FLAG):包的型別,用于操作TCP的狀態機,其8位狀態分別表示如下含義
- CWR(Congestion Window Reduce) 0x80:擁塞視窗減少標志set by sender,用來表明它接收到了設定ECE標志的TCP包,并且sender 在收到訊息之后已經通過降低發送視窗的大小來降低發送速率,
- ECE(ECN Echo) 0x40:ECN回應標志被用來在TCP3次握手時表明一個TCP端是具備ECN功能的,在資料傳輸程序中也用來表明接收到的TCP包的IP頭部的ECN被設定為11,
- URG(Urgent) 0x20:該標志位表示緊急(The urgent pointer) 標志有效,設定為1時,首部中的緊急指標有效;為0時,緊急指標沒有意義,緊急資料不進入接識訓沖區直接交給上層行程處理;
- ACK 0x10:取值1代表Acknowledgment Number欄位有效,這是一個確認的TCP包,取值0則不是確認包,后續文章介紹中當ACK標志位有效的時候我們稱呼這個包為ACK包,使用大寫的ACK稱呼,
- PSH(Push) 0x08:該標志置位時,一般是表示發送端快取中已經沒有待發送的資料,接收端不將該資料進行佇列處理,而是盡可能快將資料轉由應用處理,在處理 telnet 或 rlogin 等互動模式的連接時,該標志總是置位的,如果PSH=1的話,就不用等到整個快取都填滿,直接把快取區中的所有資料進行交付,
- RST(Reset) 0x04:用于reset相應的TCP連接,通常在發生例外或者錯誤的時候會觸發復位TCP連接,
- SYN 0x02:同步序列編號(Synchronize Sequence Numbers)有效,該標志僅在三次握手建立TCP連接時有效,
- FIN(Finish) 0x01:No more data from sender,當FIN標志有效的時候我們稱呼這個包為FIN包,
7、校驗位(Checksum):16位TCP頭,發送端基于資料內容計算一個數值,接收端要與發送端數值結果完全一樣,才能證明資料的有效性,接收端checksum校驗失敗的時候會直接丟掉這個資料包,CheckSum是根據偽頭+TCP頭+TCP資料三部分進行計算的,
8、緊急指標(Urgent Pointer):16位,指向后面是優先資料的位元組,在URG標志設定了時才有效,如果URG標志沒有被設定,緊急域作為填充,
9、選項(Option):長度不定,但長度必須以是32bits的整數倍,常見的選項包括MSS、SACK、Timestamp等等,
二、TCP狀態機
關于TCP的狀態機理解我們從幾張經典的示意圖開始
TCP狀態轉換圖

TCP三次握手、四次揮手時序圖

基于TCP的網路編程中鏈接的建立斷開、資料發送都是依賴TCP狀態轉換實作的,例如所謂的建立鏈接并不是真正的鏈接,而是一種狀態的維持,表面上的鏈接其實是通訊雙方共同維護了一個“鏈接狀態”,而建立鏈接--資料傳輸--斷開鏈接的TCP通信程序,也是這些狀態轉換的程序,這其中狀態的轉換一部分是收到或發送的某個控制位欄位的變化而引起的,如SYN、FIN、ACK等,還有一些是由于應用程式的動作或計時器超時引發的,
了解了以上的內容,下面我們就結合實際報文資料,對TCP鏈接三次握手,資料傳輸,斷開四次揮手,進行一個簡單的跟蹤驗證;
三、TCP通訊
1、三次握手
建立鏈接的三次握手的作用主要是初始化Sequence Number 的初始值,同時把這個值通過Synchronize Sequence Numbers(SYN包)告知對端,
通過Wireshark可以捕獲到三次握手的報文

握手流程:
- 1、client首先初始化該值,發送一個SYN包給server端,告訴server端一個初始化的SN值
- 2、server收到client發送的資料,回復一包資料,包括ACK確認與SYN, 既要告訴client端收到了資料,同時告知對方SYN值;
- 3、client回復ACK確認包,告知server端收到了資料;
2、資料傳輸
通過Wireshark,我們可以看下TCP傳輸中一包資料的組成,對照前面的協議組成,可以看到這里我們發送的是0x11,0x11兩個位元組的資料

可以看到接收一段回復的確認包里ACK從1變成了3,為保證資料的順序性與可靠性,TCP是有一整套的機制來控制的,如大家熟悉的滑動視窗、超時重傳等;

這里有一個需要注意的細節,這里ACK確認號的真實值其實是從0xffeb49ed 變為 0xffed49ef的,這是由于當某個主機開啟一個TCP會話時,他的初始序列號與確認號是隨機的,可能是0和4,294,967,295之間的任意值,在Wireshark里顯示的都是相對序列號/確認號,而不是實際序列號/確認號,相對序列號/確認號是和TCP會話的初始序列號相關聯的,這里Wireshark為方便大家跟蹤查看顯示的是相對值,因為比起真實序列號/確認號,跟蹤更小的相對序列號/確認號會相對容易一些,
3、四次揮手
斷開鏈接的四次揮手的作用主要是回收資源,停止資料傳輸,由于TCP是全雙工的,需要client與server兩端分別斷開各自的通向對方的通道,
通過Wireshark可以捕獲到四次揮手的報文

揮手流程:
- 1、client端發送一個FIN包告訴server服務端已經沒有資料要傳輸了,準備斷開鏈接;
- 2、server端回復一個ACK確認包,也就是告訴cient端,好,我知道你要斷開了;
- 3、server端這時要看自己還有沒有資料要發送給client,如果沒有了,也要發送一個FIN包告訴client端,我也沒有資料要傳輸了,準備斷開鏈接;
- 4、client端回復一個ACK確認包,告訴server端,好的,我知道你要斷開了;
四次揮手的流程中,sever端在接收到client端的斷開要求后,ACK確認包與FIN包是否可以合并為一個包來發送,也就是四次揮手是否可能變成三次揮手,答案是可能的,但由于TCP是全雙工的,server端與client端資料傳輸的終止在時序上是獨立且可能相隔較長時間,那么一般情況下一個完整的斷開鏈接操作都是需要四次揮手來完成的,
到這里針對TCP協議,以及鏈接->傳輸->斷開鏈接流程的基本介紹與說明就結束了,后續針對網路編程這一塊我會接著寫幾篇文章,一是對自己作業中涉及到一些網路編程的內容進行梳理與總結,另一方面希望能從下至上的加強自己對網路編程這塊認知的深度,也希望對大家能有所幫助,其中如有不足與不正確的地方還望指出與海涵,
關注微信公眾號,查看更多技術文章,

轉載說明:未經授權不得轉載,授權后務必注明來源(注明:來源于公眾號:架構空間, 作者:大凡)
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/179618.html
標籤:Java
