公司的同事們在分析網頁加載慢的問題,突然使用到了Wireshark工具,我就像發現新大陸同樣好奇,趕忙看了看,順便複習了一下相關協議。上學時學的忘的差很少了,汗顏啊!windows
/*數據幀定義,頭14個字節,尾4個字節*/ typedef struct _MAC_FRAME_HEADER { char m_cDstMacAddress[6]; //目的mac地址 char m_cSrcMacAddress[6]; //源mac地址 short m_cType; //上一層協議類型,如0x0800表明上一層是IP協議,0x0806爲arp }__attribute__((packed))MAC_FRAME_HEADER,*PMAC_FRAME_HEADER;
/*IP頭定義,共20個字節*/ typedef struct _IP_HEADER { char m_cVersionAndHeaderLen; //版本信息(前4位),頭長度(後4位) char m_cTypeOfService; // 服務類型8位 short m_sTotalLenOfPacket; //數據包長度 short m_sPacketID; //數據包標識 short m_sSliceinfo; //分片使用 char m_cTTL; //存活時間 char m_cTypeOfProtocol; //協議類型 short m_sCheckSum; //校驗和 unsigned int m_uiSourIp; //源ip unsigned int m_uiDestIp; //目的ip } __attribute__((packed))IP_HEADER, *PIP_HEADER ;
版本(Version)字段:佔4比特。用來代表IP協議實現的版本號,當前通常爲IPv4,即0100。服務器
報頭長度(Internet Header Length,IHL)字段:佔4比特。是頭部佔32比特的數字,包括可選項。普通IP數據報(沒有任何選項),該字段的值是5,即160比特=20字節。此字段最大值爲60字節。網絡
服務類型(Type of Service ,TOS)字段:佔8比特。其中前3比特爲優先權子字段(Precedence,現已被忽略)。第8比特保留未用。第4至第7比特分別表明延遲、吞吐量、可靠性和花費。當它們取值爲1時分別表明要求最小時延、最大吞吐量、最高可靠性和最小費用。這4比特的服務類型中只能置其中1比特爲1。能夠全爲0,若全爲0則表示通常服務。服務類型字段聲明瞭數據報被網絡系統傳輸時能夠被怎樣處理。例如:TELNET協議可能要求有最小的延遲,FTP協議(數據)可能要求有最大吞吐量,SNMP協議可能要求有最高可靠性,NNTP(Network News Transfer Protocol,網絡新聞傳輸協議)可能要求最小費用,而ICMP協議可能無特殊要求(4比特全爲0)。實際上,大部分主機會忽略這個字段,但一些動態路由協議如OSPF(Open Shortest Path First Protocol)、IS-IS(Intermediate System to Intermediate System Protocol)能夠根據這些字段的值進行路由決策。併發
總長度字段:佔16比特。指明整個數據報的長度(以字節爲單位)。最大長度爲65535字節。tcp
標誌字段:佔16比特。用來惟一地標識主機發送的每一份數據報。一般每發一份報文,它的值會加1。工具
標誌位字段:佔3比特。標誌一份數據報是否要求分段。學習
段偏移字段:佔13比特。若是一份數據報要求分段的話,此字段指明該段偏移距原始數據報開始的位置。大數據
生存期(TTL:Time to Live)字段:佔8比特。用來設置數據報最多能夠通過的路由器數。由發送數據的源主機設置,一般爲3二、6四、128等。每通過一個路由器,其值減1,直到0時該數據報被丟棄。ui
協議字段:佔8比特。指明IP層所封裝的上層協議類型,如ICMP(1)、IGMP(2) 、TCP(6)、UDP(17)等。操作系統
頭部校驗和字段:佔16比特。內容是根據IP頭部計算獲得的校驗和碼。計算方法是:對頭部中每一個16比特進行二進制反碼求和。(和ICMP、IGMP、TCP、UDP不一樣,IP不對頭部後的數據進行校驗)。
源IP地址、目標IP地址字段:各佔32比特。用來標明發送IP數據報文的源主機地址和接收IP報文的目標主機地址。
可選項字段:佔32比特。用來定義一些任選項:如記錄路徑、時間戳等。這些選項不多被使用,同時並非全部主機和路由器都支持這些選項。可選項字段的長度必須是32比特的整數倍,若是不足,必須填充0以達到此長度要求。
/*TCP頭定義,共20個字節*/ typedef struct _TCP_HEADER { short m_sSourPort; // 源端口號16bit short m_sDestPort; // 目的端口號16bit unsigned int m_uiSequNum; // 序列號32bit unsigned int m_uiAcknowledgeNum; // 確認號32bit short m_sHeaderLenAndFlag; // 前4位:TCP頭長度;中6位:保留;後6位:標誌位 short m_sWindowSize; // 窗口大小16bit short m_sCheckSum; // 檢驗和16bit short m_surgentPointer; // 緊急數據偏移量16bit }__attribute__((packed))TCP_HEADER, *PTCP_HEADER; /*TCP頭中的選項定義 kind(8bit)+Length(8bit,整個選項的長度,包含前兩部分)+內容(若是有的話) KIND = 1表示 無操做NOP,無後面的部分 2表示 maximum segment 後面的LENGTH就是maximum segment選項的長度(以byte爲單位,1+1+內容部分長度) 3表示 windows scale 後面的LENGTH就是 windows scale選項的長度(以byte爲單位,1+1+內容部分長度) 4表示 SACK permitted LENGTH爲2,沒有內容部分 5表示這是一個SACK包 LENGTH爲2,沒有內容部分 8表示時間戳,LENGTH爲10,含8個字節的時間戳 */
源、目標端口號字段:佔16比特。TCP協議經過使用"端口"來標識源端和目標端的應用進程。端口號可使用0到65535之間的任何數字。在收到服務請求時,操做系統動態地爲客戶端的應用程序分配端口號。在服務器端,每種服務在"衆所周知的端口"(Well-Know Port)爲用戶提供服務。
順序號字段:佔32比特。用來標識從TCP源端向TCP目標端發送的數據字節流,它表示在這個報文段中的第一個數據字節。
確認號字段:佔32比特。只有ACK標誌爲1時,確認號字段纔有效。它包含目標端所指望收到源端的下一個數據字節。
頭部長度字段:佔4比特。給出頭部佔32比特的數目。沒有任何選項字段的TCP頭部長度爲20字節;最多能夠有60字節的TCP頭部。
/*UDP頭定義,共8個字節*/ typedef struct _UDP_HEADER { unsigned short m_usSourPort; // 源端口號16bit unsigned short m_usDestPort; // 目的端口號16bit unsigned short m_usLength; // 數據包長度16bit unsigned short m_usCheckSum; // 校驗和16bit }__attribute__((packed))UDP_HEADER, *PUDP_HEADER;
因爲維基百科專美與前,沒法寫的比其更好,因此,貼它的連接在這裏:
https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE#.E8.BF.90.E4.BD.9C.E6.96.B9.E5.BC.8F
主要學習它的運做方式部分,就能較好的理解操做過程了。
wireshark以太網幀的封包格式爲:
Frame=Ethernet Header +IP Header +TCP Header +TCP Segment Data
Ethernet Header如下的IP數據報最大傳輸單位爲MTU(Maximum Transmission Unit,Effect of short board),對於大多數使用以太網的局域網來講,MTU=1500。
TCP數據包每次可以傳輸的最大數據分段爲MSS,爲了達到最佳的傳輸效能,在創建TCP鏈接時雙方將協商MSS值——雙方提供的MSS值中的最小值爲此次鏈接的最大MSS值。MSS每每基於MTU計算出來,一般MSS=MTU-sizeof(IP Header)-sizeof(TCP Header)=1500-20-20=1460。
這樣,數據通過本地TCP層分段後,交給本地IP層,在本地IP層就不須要分片了。可是在下一跳路由(Next Hop)的鄰居路由器上可能發生IP分片!由於路由器的網卡的MTU可能小於須要轉發的IP數據報的大小。
這時候,在路由器上可能發生兩種狀況:
(1)若是源發送端設置了這個IP數據包能夠分片(May Fragment,DF=0),路由器將IP數據報分片後轉發。
(2)若是源發送端設置了這個IP數據報不能夠分片(Don’t Fragment,DF=1),路由器將IP數據報丟棄,併發送ICMP分片錯誤消息給源發送端。
一個簡單的請求示例截圖如圖:
從左到有依次爲no(Frame編號)、Time(時間)、Source(源地址)、Destination(目的地址)、Protocal(協議)、Length(包大小)、Info(詳細信息)。
tcp請求與回覆的info中包括:端口信息(如63703->8279)表示src.port -> des.port,標誌位信息(如SYN、ACK)、Seq信息、Ack信息(注意,這個ack是確認號字段m_uiAcknowledgeNum)、len(上層數據長度)、MSS(mss長度)、WS(窗口大小字段)。
實際操做中,頭部爲66字節,是由於加了12字節的tcp選項信息。實際MSS爲1460.
每條數據的詳細信息均可以在選中數據後在下方顯示,如圖:
從上到下依次爲Frame(整個楨信息)、Ethernet II(以太頭信息)、Internet Protocal Version(IP頭信息)、Transmission Control Protocal(TCP頭信息),點開後每個字段的詳細信息均可顯示,與協議一致。
結合實踐分析tcp運做方式
三次握手:tcp經過三次握手建立連接,如圖,
首先63703->8279 發送SYN,Seq=0;而後8279->63703, 發送SYN&&ACK, Seq=0, Ack=1;最後63703->8279 發送ACK,Seq=1,Ack=1。這樣就完成了三次握手。
與維基百科中的相關知識,作對照,發現徹底符合實踐:
1.客戶端經過向服務器端發送一個SYN來建立一個主動打開,做爲三路握手的一部分。客戶端把這段鏈接的序號設定爲隨機數A。 首先63703->8279 發送SYN,Seq=0 A=0.
2.服務器端應當爲一個合法的SYN回送一個SYN/ACK。ACK的確認碼應爲A+1,SYN/ACK包自己又有一個隨機序號B。 而後8279->63703, 發送SYN&&ACK, Seq=0, Ack=1 B=0
3.最後,客戶端再發送一個ACK。當服務端受到這個ACK的時候,就完成了三路握手,並進入了鏈接建立狀態。此時包序號被設定爲收到的確認號A+1,而響應則爲B+1。 最後63703->8279 發送ACK,Seq=1,Ack=1。這樣就完成了三次握手。
數據傳輸:69和70是一個http請求,由於包過長,切分紅兩個tcp包。75和76分別對這兩個包進行ack響應,告知已經收到。81則對請求作了業務上的響應返回。而82則是對81的ack響應。(未貼出數據:69:Seq=1,Ack=1,Len=1448 ; 70:Seq=1449,Ack=1,Len=112 ; 81:Seq=1, Ack=1561, Len=123)。分析上述幾條數據咱們發現,Seq的遞增主要是看上一條發送數據的Len,如Seq70 = Seq69 + Len69。而一條數據的ack信息的ack值也是看請求數據的seq和len。如75是對69的ack,則Ack75=Seq69+Len69。這一規律也符合協議規定。具體見維基百科數據傳輸舉例。固然,也有選擇確認(Selective Acknowledgement)的示例,就不貼出來了。
選擇確認:TCP報文的接收者爲了確保可靠性,在接收到必定數量的連續字節流後才發送確認。
Wireshark(前稱Ethereal)是一個網絡封包分析軟件。網絡封包分析軟件的功能是擷取網絡封包,並儘量顯示出最爲詳細的網絡封包資料。Wireshark使用WinPCAP做爲接口,直接與網卡進行數據報文交換。做爲一個免費的軟件,真的很是好用。
參考資料:
1. 傳輸控制協議,維基百科,https://zh.wikipedia.org/wiki/%E4%BC%A0%E8%BE%93%E6%8E%A7%E5%88%B6%E5%8D%8F%E8%AE%AE#.E5.BB.BA.E7.AB.8B.E9.80.9A.E8.B7.AF
2.TCP通訊流程解析,CSDN博客,http://blog.csdn.net/phunxm/article/details/5836034
3.IP頭、TCP頭、UDP頭詳解以及定義,CSDN博客,http://blog.csdn.net/mrwangwang/article/details/8537775#comments