網絡傳輸數據封裝詳解(IP,UDP,TCP)


IP數據包也叫IP報文分組,傳輸在ISO網絡7層結構中的網絡層,它由IP報文頭和IP報文用戶數據組成,IP報文頭的長度一般在20到60個字節之間,而一個IP分組的最大長度則不能超過65535個字節。 
下圖為IP分組的報文頭格式,報文頭的前20個字節是固定的,后面的可變。

 
版本:占4位(bit),指IP協議的版本號。目前的主要版本為IPV4,即第4版本號,也有一些教育網和科研機構在使用IPV6。在進行通信時,通信雙方的IP協議版本號必須一致,否則無法直接通信。 
首部長度:占4位(bit),指IP報文頭的長度。最大的長度(即4個bit都為1時)為15個長度單位,每個長度單位為4字節(TCP/IP標准,DoubleWord),所以IP協議報文頭的最大長度為60個字節,最短為上圖所示的20個字節。 
服務類型:占8位(bit),用來獲得更好的服務。其中的前3位表示報文的優先級,后面的幾位分別表示要求更低時延、更高的吞吐量、更高的可靠性、更低的路由代價等。對應位為1即有相應要求,為0則不要求。 
總長度:16位(bit),指報文的總長度。注意這里的單位為字節,而不是4字節,所以一個IP報文的的最大長度為65535個字節。 
標識(identification):該字段標記當前分片為第幾個分片,在數據報重組時很有用。 
標志(flag):該字段用於標記該報文是否為分片(有一些可能不需要分片,或不希望分片),后面是否還有分片(是否是最后一個分片)。 
片偏移:指當前分片在原數據報(分片前的數據報)中相對於用戶數據字段的偏移量,即在原數據報中的相對位置。 
生存時間:TTL(Time to Live)。該字段表明當前報文還能生存多久。每經過1ms或者一個網關,TTL的值自動減1,當生存時間為0時,報文將被認為目的主機不可到達而丟棄。使用過Ping命令的用戶應該有印象,在windows中輸入ping命令,在返回的結果中即有TTL的數值。 
協議:該字段指出在上層(網絡7層結構或TCP/IP的傳輸層)使用的協議,可能的協議有UDP、TCP、ICMP、IGMP、IGP等。 
首部校驗和:用於檢驗IP報文頭部在傳播的過程中是否出錯,主要校驗報文頭中是否有某一個或幾個bit被污染或修改了。 
源IP地址:32位(bit),4個字節,每一個字節為0~255之間的整數,及我們日常見到的IP地址格式。 
目的IP地址:32位(bit),4個字節,每一個字節為0~255之間的整數,及我們日常見到的IP地址格式。

由於Delphi里面沒有位域這個概念,所以定義結構的時候只能整字節了,挺懷戀C++或者Erlang的,有位域定義出來和使用起來都很方便了 
//IP包 
  TIPHeader = packed record 
    iph_verlen: byte;           // 版本和長度 
    iph_tos: byte;              // 服務類型 
    iph_length: word;           // 總長度,2個無符號字節所以只能65535 
    iph_id: word;               // 標識 
    iph_offset: word;           // 標志和片偏移 
    iph_ttl: byte;              // 生存時間 
    iph_protocol: byte;         // 協議 
    iph_xsum: word;             // 頭校驗和 
    iph_src: longword;          // 源地址 
    iph_dest: longword;         // 目的地址 
  end;

這個結構體有什么用呢?其實在嗅探的時候就很有用了.

 

 

 

TCP數據包的頭

IP/TCP/UDP頭數據結構定義 - Jimmy - jimmyloveforever

typedef struct _TCP_HEADER {
 USHORT nSourPort ;   // 源端口號16bit
 USHORT nDestPort ;   // 目的端口號16bit
 UINT nSequNum ;   // 序列號32bit
 UINT nAcknowledgeNum ; // 確認號32bit
 USHORT nHLenAndFlag ;  // 前4位:TCP頭長度;中6位:保留;后6位:標志位16bit
 USHORT nWindowSize ;  // 窗口大小16bit
 USHORT nCheckSum ;   // 檢驗和16bit
 USHORT nrgentPointer ;  // 緊急數據偏移量16bit
} TCP_HEADER, *PTCP_HEADER ;

 

UDP數據包的頭

IP/TCP/UDP頭數據結構定義 - Jimmy - jimmyloveforever

typedef struct _UDP_HEADER {
 USHORT nSourPort ;   // 源端口號16bit
 USHORT nDestPort ;   // 目的端口號16bit
 USHORT nLength ;   // 數據包長度16bit
 USHORT nCheckSum ;   // 校驗和16bit
} UDP_HEADER, *PUDP_HEADER ;

進入協議棧的過程:(從協議棧出來剛好相反)


ICMP頭和報文校驗和的計算

 

 ICMP頭和報文校驗和的計算 - Jimmy - jimmyloveforever 

////////////////////////////////定義ICMP包頭
typedef struct _ICMP_HEADER {
 BYTE bType ;   // 類型8bit
 BYTE bCode ;   // 代碼8bit
 USHORT nCheckSum ;  // 校驗和16bit
 USHORT nId ;   // 標識,本進程ID16bit
 USHORT nSequence ;  // 序列號16bit
 UINT nTimeStamp ; // 可選項,這里為時間,用於計算時間32bit
} ICMP_HEADER, *PICMP_HEADER ;

/////////////////////////////////

發送ICMP報文時,必須由程序自己計算校驗和,將它填入ICMP頭部對應的域中。校驗和的計算方法是:

將數據以字(16位)為單位累加到一個雙字中(強轉換雙字類型),如果數據長度為奇數(奇數個字節),最后一個字節將被擴展到字,累加的結果是一個雙字,

最后將這個雙字的高16位和低16位相加后取反,便得到了校驗和!

// 計算ICMP包校驗值
// 參數1:ICMP包緩沖區
// 參數2:ICMP包長度
USHORT GetCheckSum ( LPBYTE lpBuf, DWORD dwSize )
{
 DWORD dwCheckSum = 0 ;
 USHORT* lpWord = (USHORT*)lpBuf ;

 // 累加
 while ( dwSize > 1 )
 {
  dwCheckSum += *lpWord++ ;
  dwSize -= 2 ;
 }

 // 如果長度是奇數
 if ( dwSize == 1 )
  dwCheckSum += *((LPBYTE)lpWord) ;

 // 高16位和低16位相加
 dwCheckSum = ( dwCheckSum >> 16 ) + ( dwCheckSum & 0xFFFF ) ;

 // 取反
 return (USHORT)(~dwCheckSum ) ;
}

 

轉載地址:http://blog.163.com/tianshuai11@126/blog/static/618945432011101110497885/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM