TCP協議簡介
tcp/ip協議族中傳輸層最重要的兩種協議是UDP和TCP協議,上一篇文章用很短的篇幅介紹完了UDP協議相關的內容,但相對於UDP而言的TCP協議,是種更復雜,應用更廣的協議。在接下來的幾篇文章中都會學習TCP協議相關的知識。這裡補充一點有用的小知識:之前分析網路包我都用的tcpdump命令,因為寫部落格時都在用ubuntu系統,所以linux下的tcpdump簡單強大,也不用安裝什麼。現在寫文章時換回了windows7系統,因為之前在ubuntu下寫一篇文章時瀏覽器總是莫名的把寫了半天的文章搞丟,所以換回windows寫文章。在windows下分析網路包用的wireshark,一個你不得不知道的專業分析網路包的工具。
首先還是介紹下TCP協議的rfc定義文件內容。RFC793 定義了TCP協議。
首先我們要清楚TCP在整個TCP/IP協議族中的位置。它基於IP協議之上,服務於更高層的應用層協議,如ftp,http,smtp等等。
TCP的頭部格式(Header Format)如下:
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Source Port | Destination Port | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Sequence Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Acknowledgment Number | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Data | |U|A|P|R|S|F| | | Offset| Reserved |R|C|S|S|Y|I| Window | | | |G|K|H|T|N|N| | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Checksum | Urgent Pointer | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | Options | Padding | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | data | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
(給大家介紹一個畫ascii圖的網址:http://www.asciiflow.com/#Draw)
- Source Port: 源埠。我們知道ip報文中有源ip地址和目的ip地址,這兩個值加上tcp報文中的源埠(source port)和目的埠(destination port),就確定了一個tcp連線。
- Destination Port: 目的埠。如source port介紹一樣。 一般對於一個ip地址和一個port埠號,我們也稱為一個socket。說起socket我們應該並不陌生,在應用開發時經常用到。socket就是對網路程式設計介面的封裝。
- Sequence Number: 序列號。我們知道tcp是面向流的連線。tcp針對每個傳送的位元組,都會進行計數。序號是32bit的無符號數。值範圍是0~232-1,當達到最大值時會從又從0開始計數。
- Acknowledgment Number: 確認序號。指期望下次接收到的序列號。
- Data offset: TCP報文頭部長度。以32位(8byte)為單位。所以最大報議長長度是60位元組。如果沒有可選欄位的話,一般是20位元組。
- Reserved: 保留欄位
- U,A,P,R,S,F: 六個標誌位。URG:緊急指標 ACK:確認序號有效 PSH:接收方應該儘快將該報文交給應用層 RST:重新連線 SYN:同步序號發起一個連線 FIN:發端完成傳送任務
- window: TCP流量的視窗大小。以位元組為單位。最大是216 - 1 = 65535
- Checksum: 檢驗和,覆蓋了整個TCP的頭部和資料。同傳送端計算,接收端驗證。
- Urgent Pointer: 緊急指標是一個正的偏移量,它和序號欄位相加表示緊急資料的最後一個位元組序號
- options: 可選欄位。最常見的可選欄位如MSS(maximum segment size)。
- Padding: 填充欄位。因為頭部欄位必需是32位的整數倍。所以當可選欄位非32整數倍長度時,用0來填充。
- data: tcp資料
TCP連線建立
TCP連線的建立,我想很多找工作的時候都喜歡問這個問題,比如說說TCP連線的三次握手(three way handshake)的過程。所以搞清楚tcp連線建立的過程至少對找工作還是挺有幫助的。
- client端傳送一個syn初始化同步序列號,假設seq=0x9e0dd824, 我們這裡用16進表示。上圖中標示seq=0的表示方法是相對錶示法,這樣以後出現的seq序列號都是相對於syn時的初始序列號的數。
- server端收到客戶端的syn報文後,同時也傳送一個[syn,ack]的報文。注意這裡的seq=0表示server端的初始化序列,也是一個相對值,它的真實值可能是seq=0x59bc0bd5。這時傳送的報文中設定了ack標識,並置確認序列號(acknowledge number)為ack=1,即為之前收到的[syn]中seq的初始化值加1,真實值為ack=0x9e0dd825
- client傳送一個[ack]報文。seq=1,即為seq=0x9e0dd825, ack=1指對server端上次傳送的seq加1 ,即ack=0x59bc0bd6
現在經過這三步(三次握手),已經建立起了一個tcp連線。這裡要強調的上圖中沒有所謂的哪邊是客戶端,哪邊是服務端,都只是相對而言的。
TCP連線終止
tcp的關閉有較連線建立有點複雜,因為我們知道tcp連線是全雙工的,即端與端之前可以同時都可以傳送,接收資料。那麼關閉連線理論上是可以有多種組合的。
下面來看一種正常情況下的關閉流程:
- clinet端主動發起關閉操作,傳送一個[fin,ack]的報文。
- server端在收到client端傳送的[fin,ack]報文後,也回覆一個[fin,ack]報文
- client端最後傳送一個[ack],此時連線終止過程就完成了。
還有一種情況,就是一端完成關閉通知後,它還可以接收別一端繼續傳輸過來的資料直到別一端也通知連線在這個方向上也關閉了。這就是所謂的tcp連線半關閉。如圖:
- client端傳送[fin,ack]報文通知server端它已經結束在這個方向上的資料傳輸。
- server端傳送[ack]確認
- 此時client-->server方向上已經不能再傳輸任何資料了。但是server-->client方向上還可以繼續傳輸資料。
- 當server完成了這個方向上的資料傳輸時,它現在要關閉這個方向上的連線,則要傳送[fin,ack]報文
- client最後一次傳送[ack]確認
TCP狀態變遷
上圖說明:實線箭頭方向是客戶端的正常狀態變遷,虛線箭頭是服務端的正常狀態變遷,如果大家要上機實踐下,可以用netstat命令檢視本機的所有連線狀態。結合wireshark截包分析下。
用序列圖來表示整個連線建立和終止的過程中狀態的變遷過程:
其實這兩個圖描述的tcp狀態變遷過程一樣,只是表現形式不同。可以從這兩個圖中明確的學習到tcp的狀態變化。