在編寫網絡程序時,常使用TCP協議。那么一個tcp包到底由哪些東西構成的呢?其實一個TCP包,首先需要通過IP協議承載,而IP報文,又需要通過以太網傳送。下面我們來看看幾種協議頭的構成
一 .Ethernet頭
以太幀分好幾種類型,常見的以太幀為Ethernet II
下面就是一個典型的Ethernet II幀
Ethernet II類型以太網幀的最小長度為64字節(6+6+2+46+4),最大長度為1518字節(6+6+2+1500+4)
首先是目的MAC 6個字節,然后源MAC6個字節,接下來數據類型兩個字節。
常見的類型如下
IPv4: 0x0800
ARP:0x0806
PPPoE:0x8864
802.1Q tag: 0x8100
IPV6: 0x86DD
MPLS Label:0x8847
然后是數據長度,46-1500字節。對于不定長的數據包,幀最后還有4個字節的FCS(Frame check sequence)
下面是一個以太幀頭示例,該報文類型為IPv4(0x8000)
二 IP頭部
對于一個IPv4類型的以太幀,數據的開始就是IP頭部。一般IPv4的頭部是20個字節。
版本號(Version):4bit。表明IP協議的版本號。一般為0100(IPv4),0110(IPv6)
IP包頭長度(Header Length):4bit。用于描述IP包頭長度,因為IP包頭長度是可變的。
這里所指示的長度,是以4個字節為一個單位。例如,一個IP包頭的長度最長為“1111”,即15*4=60個字節。IP包頭最小長度為20字節。
服務類型(Type of Service):長度8比特。
IP包總長(Total Length):16bit。 以字節為單位計算的IP包的長度 (包括頭部和數據),所以IP包最大長度65535字節。
標識符(Identifier):16bit。該字段和Flags和Fragment
Offest字段聯合使用,對較大的上層數據包進行分段(fragment)操作。路由器將一個包拆分后,所有拆分開的小包被標記相同的值,以便目的端設備能夠區分哪個包屬于被拆分開的包的一部分。
標記(Flags):3bit。第一位是保留位不使用。第二位是DF(Don't
Fragment)位,DF位設為1時表明路由器不能對該數據包分包。如果一個數據包無法在不分段的情況下發送,則路由器會丟棄該數據包并返回一個錯誤信息。第三位是MF(More Fragments)位,當路由器對一個上層數據包分段,則路由器會在除了最后一個分段的IP包的包頭中將MF位設為1。
片偏移(Fragment Offset):13bit。表示該IP包在該組分片包中位置,接收端靠此來組裝還原IP包。
生存時間(TTL):8bit。當IP包在網絡上傳送時,每經過一個路由器,TTL就自動減一。值為0時,則丟棄報文。防止報文進入環路
協議(Protocol):8bit。標識IP頭后面的報文協議類型
以下是比較常用的協議號:
1 ICMP
2 IGMP
6 TCP
17 UDP
88 IGRP
89 OSPF
頭校驗和(Header Checksum):16bit。用來做IP頭部的正確性檢測,但不包含數據部分。由于路由器會改變TTL,所以路由器會為每個通過的數據包重新計算這個值。
源和目的地址(Source and Destination Addresses):這兩個地段都是32比特。標識了這個IP包的起源和目標地址。要注意除非使用NAT,否則整個傳輸的過程中,這兩個地址不會改變。
下圖就是一個IP頭的內容
三 TCP 頭部
TCP封裝在IP報文中的時候,如下圖所示,TCP頭緊接著IP頭(IPV6有擴展頭的時候,則TCP頭在擴展頭后面),不攜帶選項(option)的TCP頭長為20bytes,攜帶選項的TCP頭最長可到60bytes。
源端口(Source Port):16bit, 表示報文發送方的端口號
目的端口(Destination port): 16bit,表示報文接收方的端口號
序列號(SN):32bit,標識了TCP報文中第一個byte在對應方向的傳輸中對應的字節序號。當SYN出現,序列碼實際上是初始序列碼(ISN),而第一個數據字節是ISN+1,單位是byte。比如發送端發送的一個TCP包凈荷(不包含TCP頭)為12byte,SN為5,則發送端接著發送的下一個數據包的時候,SN應該設置為5+12=17。通過系列號,TCP接收端可以識別出重復接收到的TCP包,從而丟棄重復包,同時對于亂序數據包也可以依靠系列號進行重排序,進而對高層提供有序的數據流。另外SYN標志和FIN標志在邏輯上也占用一個byte,當SYN標志位有效的時候,該字段也稱為ISN(initial sequence number),詳細請參考后續的TCP連接管理。
應答號(ACK):32bit,標識了報文發送端期望接收的字節序列。如果設置了ACK控制位,這個值表示一個準備接收的包的序列碼,注意是準備接收的包,比如當前接收端接收到一個凈荷為12byte的數據包,SN為5,則發送端可能會回復一個確認收到的數據包,如果這個數據包之前的數據也都已經收到了,這個數據包中的ACK Number則設置為12+5=17,表示17byte之前的數據都已經收到了。在舉一個例子,如果在這個數據包之前有個SN為3,凈荷為2byte的數據包丟失,則在接受端接收到這個SN為5的亂序數據包的時候,協議要求接收端必須要回復一個ACK確認包,這個確認包中的Ack Number只能設置為3。
頭長(Header Length):4bit,指示TCP頭的長度,即數據從何處開始。最大為15,單位是32比特,即4個字節,與IP頭中的長度定義相同。
保留(Reserved):4bit,這些位必須是0。為了將來定義新的用途所保留,其中RFC3540將Reserved字段中的最后一位定義為Nonce標志。
標志(Code Bits):8bit
CWR(Congestion Window Reduce):擁塞窗口減少標志被發送主機設置,用來表明它接收到了設置ECE標志的TCP包,發送端將通過降低發送窗口的大小來降低發送速率
ECE(ECN Echo):ECN響應標志被用來在TCP3次握手時表明一個TCP端是否具備ECN功能,并且表明接收到的TCP包的IP頭部的ECN被設置為11。更多信息請參考RFC793。
URG(Urgent):表示緊急(The urgent pointer) 指針是否有效。
ACK(Acknowledgment):1表示這是一個確認的TCP包, 0則不是確認包。
PSH(Push):該標志置位時,一般是表示發送端緩存中已經沒有待發送的數據,接收端不將該數據進行隊列處理,而是盡可能快將數據轉由應用處理。
RST(Reset):用于復位相應的TCP連接。通常在發生異常或者錯誤的時候會觸發復位TCP連接。
SYN(Synchronize):表示同步序列編號(Synchronize Sequence Numbers)是否有效。該標志僅在三次握手建立TCP連接時有效。它提示TCP連接的服務端檢查序列編號,該序列編號為TCP連接發起端(一般是客戶端)的初始序列編號。在這里,可以把TCP序列編號看作是一個范圍從0到4,294,967,295的32位計數器。通過TCP連接交換的數據中每一個字節都經過序列編號。在TCP報頭中的序列編號欄包括了TCP分段中第一個字節的序列編號。
FIN(Finish):帶有該標志置位的數據包用來結束一個TCP會話,但對應端口仍處于開放狀態,準備接收后續數據。當FIN標志有效的時候我們稱呼這個包為FIN包。
窗口大小(Window Size):16bit,表示從Ack Number開始還能接收多少字節的數據量,即當前接收端的接收窗口還有多少剩余空間。用于TCP的流量控制。
校驗和(Checksum):16bit。發送端基于數據內容計算一個數值,接收端要與發送端數值結果完全一樣,才能證明數據的有效性。接收端checksum校驗失敗的時候會直接丟掉這個數據包。CheckSum是根據偽頭+TCP頭+TCP數據三部分進行計算的。
緊急指針(Urgent Pointer):16位,在URG標志設置了時才有效。與序號字段的值相加后表示最后一個緊急數據的下一字節的序號,可以說這個字段是緊急指針相對當前序號的偏移。
選項(Option):長度不定,但長度必須以是32bits的整數倍。常見的選項包括MSS、SACK、Timestamp等等,后續的內容會分別介紹相關選項。
一個完整的TCP頭展示
文章列表