Win32如何定義IP資料報的首部

2021-06-22 07:10:06 字數 3046 閱讀 3940

文章出自:

在進行網路程式設計時,可能需要直接操作原始的ip資料報,例如編寫網路嗅探器。此時要定義乙個表示ip資料報首部的結構體來獲取首部中的各個資訊,問題也隨之而來:平時我們使用的資料都是byte、word或者dword,但ip資料報首部的有些欄位並不按照位元組、字或雙字對齊,欄位的長度也不是一位元組、兩位元組或四位元組,這種不一致的現象使得結構體的定義很有難度。我見過幾種ip資料報首部結構體的定義,雖然方法各異,但都是大量使用union,將幾個字段塞進乙個byte或word中,在**中還要通過移位、按位與等操作獲取實際的字段。其實,只要理解資料在記憶體中是如何擺放的,以及利用好結構體定義的特性,可以最大限度地使結構體的字段直接對應首部中的字段,避免在**中使用大量的位操作。

使用c/c++程式設計時我們都會大量定義和使用結構體,但可能有不少人還不知道定義結構體時可以使用「位域」這個特性。位域是指不需要佔據整個字段長度,而只需要佔據乙個或多個位的字段。定義方法如下所示:

structbyte ;
上面的**定義了乙個名為byte的結構體,它有兩個欄位low和high,這兩個欄位的型別都是byte,而且都指定了只占用四個位,所以它們共用乙個byte的空間。又因為low定義在high之前,所以low使用這個byte的低四位,high使用高四位。

位域的型別除了byte之外,還可以使用word和dword。要注意的是,在使用位域時,對於那些希望共用同乙個byte、word或dword的字段,要始終使用相同的型別來定義,只有當byte、word或dword的位分配完畢時,才使用另一種型別。例如,不要出現下面的定義:

structbitfields ;

structbitfields ;

上面的定義令人難以理解(至少我是根本無法理解),而且也不知道編譯器會如何處理這些定義。所以,為了程式的可理解性和正確性,應該中規中矩地使用位域:

structbitfields ;
有了位域,在訪問這些欄位時,編譯器會自動進行位操作,還會自動進行截斷,所以在**中再也不需操心這些繁瑣的操作了。

位元組序對大家來說都不會陌生,它表示位元組在記憶體中的存放順序。而位序與位元組序的意義相近,它表示的是位元組內每個位的存放順序,也有大端和小端格式。我們通常只在位元組級別上訪問資料,幾乎不會在位級別上訪問資料,所以忽略了位序這個問題。現在我們用到了位域,已經深入到了位級別,因此很有必要了解一下intel處理器的位序。遺憾的是,我沒找到任何資料解析intel處理器使用哪種位序,因此只能靠自己動手進行實驗。

實驗很簡單,下面是實驗的**:

voidwmain() ;

byte b = ;

b.zero = 1;

b.five = 1;

}

該**將乙個位元組的第0位和第5位設定為1,其它位都是0。由於visual studio不能以二進位制方式檢視資料,因此我使用windbg進行檢視:

左上角第乙個位元組就是變數b的資料,可以看到第0位和第5位已經設定為1。更重要的是,看到了位序是大端格式,即低位址存放高位,高位址存放低位。

了解了位域和位序之後,下面就開始定義ip資料報的首部了。

首先來看一下ip資料報首部的格式:

對於那些佔據整個byte、word或dword的字段,可以直接按照它們的先後順序來定義而不會出現任何問題,而其它「不規則」的字段則需要進行特殊的設計。「不規則」的字段包括:版本、首部長度、片偏移以及三個標識位。

首先來看一下版本和首部長度,它們都是四位元組長度,一開始我們會很自然地按照它們的先後順序來定義:

structipheader ;
然而這樣是錯誤的,你使用version欄位得到的是首部長度,使用headerlength得到的是版本,兩者調轉過來了!之所以會出現這樣的錯誤,恰恰是位序的問題。上文說過,intel處理器使用的是大端格式的位序,所以ipheader結構的第乙個位元組是這樣的:

由於version定義在前,所以version使用0~3位,headerlength使用4~7位。很明顯,這跟ip資料報首部的格式正好相反。所以,正確的定義應該是:

structipheader ;
接下來的區分服務、總長度和標識都佔據了整個byte和word的長度,所以按順序定義它們就行了:

structipheader ;
接下來的三個標識位和片偏移則有點複雜。首先來看一下乙個word中的位是如何放置的(注意是小端位元組序):

對照ip資料報首部的格式圖,可以看到三個標識位分別占用了word的7、6和5位,剩下的位都是片偏移的。雖然從圖中看上去片偏移的位都連線在一起,可是如果以位編號的順序來看的話,片偏移實際上被標識位分割成了兩部分,分別在兩個byte中:第一部分是0~4位,第二部分是8~f位。所以,無論如何也不能僅僅使用位域把這兩部分組合在一起,必須在**中使用位操作來組合。這確實是乙個遺憾。

將這兩部分組合起來有多種方法,我使用的方法是:將這個word分成兩個byte,第乙個byte 的最後三個位作為標緻位,前面5個位作為片偏移的第一部分,第二個byte全部作為片偏移的第二部分,如下所示:

structipheader ;
注意第乙個byte中的字段都按反順序來定義,這也是位序的緣故,就不再解釋了。為了得到完整的片偏移,要將第一部分左移8位,再加上第二部分:

fragmentoffset = (fragmentoffset0 << 8) + fragmentoffset1
好了,ip資料報首部中比較難搞的字段都已經解決了,剩下的都很容易解決,下面是完整的定義:

structipheader ;
既然說到了ip資料報首部,就不得不說一下tcp報文段的首部。下面是tcp報文段的首部格式:

上圖中比較複雜的是資料偏移、保留字段以及一些標緻位,不過有了上文的講解,相信這不再是問題。下面直接給出完整的定義,不再詳細解釋了:

structtcpheader ;
為了本文的完整性,這裡也給出udp報文段首部的格式以及定義。

structudpheader ;

如何自己寫Win32控制項

最早時候就曾經 看到過說所有控制項都是視窗 window 更有甚者說都是物件,這個就不扯了。自己做好的控制項是做成 lib還是 dll那是後話,mfc我是不熟悉了,win32 還是看了幾天的。大致把製作的整個流程簡要的記錄一下。自己做的控制項最主要的功能就是接受你發給他的命令,也就是要給外部呼叫的介...

win32 截圖 獲取 資料

如下 hdc hdesktop getdc getdesktopwindow int bitperpixel getdevicecaps hdesktop,bitspixel int width getdevicecaps hdesktop,horzres int height getdevicec...

win32 資料型別 vs c

1 在c 中做很多應用需要使用win32 api,但發現原型函式的一些資料型別看起來非常費勁,甚至在c 中 沒有 這種資料型別,查閱了一下資料,資料型別對應關係整理如下,希望對大家有用 2 bool system.int32 3 boolean system.int32 4 byte system....