2010-08-18 68 views
3

我有一個結構,我已經寫了,它應該代表整個UDP數據包,以及以太網頭和所有。在這裏它是:結構中的字段跳過字節

#pragma pack(1) 
struct UDPPacket { 
    // an array to hold the destination mac address of the packet 
    unsigned char dstmac[6]; 

    // an array to hold the source mac address of the packet 
    unsigned char srcmac[6]; 

    // two bytes to hold the packet type, this is almost always IP (08 00) 
    WORD ethtype; 

    // each of the subfields of this take up 4 bits. ver, the first half, 
    // is the ip version, which should usually be 4 for ipv4, and the second 
    // is the length of the header divided by 4, which is almost always 5 
    struct { 
     unsigned ver : 4; 
     unsigned len : 4; 
    } verlen; 

    // this isn't used in ipv4 and is 0 
    BYTE tos; 

    // the total length of the header + data 
    WORD iplen; 

    // the id of this datagram for reassembling fragmented packets 
    WORD id; 

    // the first subfield occupies 3 bits and is the flags of this packet, which is usually 0 
    // the second subfield is the fragmentation offset for large datagrams that have been split up for sending, usually 0 
    struct { 
     unsigned flags : 3; 
     unsigned fragmentation : 13; 
    } flagfrag; 

    // time to live; usually 35 or 128 
    BYTE ttl; 

    // the protocol with which this packet is being transported 
    // 1 = ICMP, 2 = IGMP, 6 = TCP, 17 = UDP 
    BYTE protocol; 

    // the ip checksum of this packet 
    WORD ipchecksum; 

    // the source ip of this packet 
    DWORD src; 

    // the destination ip of this packet 
    DWORD dest; 
    // the port from which this packet is coming 
    WORD srcport; 

    // the port this packet is headed to 
    WORD destport; 

    // the length of the udp header + data, not including the ip header 
    // so it's usually basically iplen - 20 
    WORD udplen; 

    // the udp checksum of this packet 
    WORD udpchecksum; 

    // a char pointer to the data of the packet 
    unsigned char data[10000]; 
}; 
#pragma pack() 

當然,這是一個真正的UDP包的表示,該字節必須在相同的偏移,因爲它們將在一個信息包,和指針於這種類型的結構的將是投到unsigned char* s發送。
我的問題是,當我嘗試在UDPPacket.verlen之後分配任何內容時,它會跳過大約5個字節並從那裏開始。例如,當我指定iplen字段時,不是將字節設置爲偏移量16和17,而是將它們指定爲類似於23和24的字符(我不能完全說明,因爲我的手機中沒有可用的程序)。
有沒有一個明顯的原因,我錯過了,或者我只是做錯了什麼?

+0

順便說一下,我在x64 Windows 7上使用Visual Studio 2008,但是爲x86編譯。 – Hock 2010-08-18 14:39:30

回答

2

#pragmas看的權利。位字段不會「自動打包」爲符合明確指定位數的最小類型。我懷疑verlen將你的給定類型「無符號」,並假設它是一個無符號整數大小的位,這聽起來像你的編譯器中的32位。請嘗試將verlen的字段改爲「無符號字符」。

更多這裏。能夠指定「無符號的字符」這裏是一個MSFT擴展(以ANSI C),但應該工作:http://msdn.microsoft.com/en-us/library/yszfawxh(VS.80).aspx

注: flagfrag同樣適用於「無符號短」。

+0

之前,我認爲它不是什麼類型,因爲你只是指定它應該佔用的位數,但現在我認爲你指定了一個佔用一定數量字節的類型(並且因此位),那麼你可以在結構中有不同的位域,只要它們加起來就是你之前指定的類型。那是對的嗎? – Hock 2010-08-18 15:13:07

+0

我相信在C++中這不是一個擴展,而是來自標準的一個要求。我將不得不再次仔細地閱讀語法,如果我爲此收集時間,我今晚可能會嘗試。 – 2010-08-18 15:20:41

+0

是的,基本上編譯器只關心你用作位域的底層類型;命名單個位的能力僅僅是一個語法上的便利 - 它不會引入新的「位」類型或類似的東西。如果這就是你要求的,那麼你不可能(儘管你應該嘗試它或者閱讀文檔),你可以在結構中的位域中間擁有完整的較小類型。 – 2010-08-18 15:20:47

0

你必須檢查你的填充和你的編譯器的對齊設置。

1

該標準不要求編譯器包裝位字段成一個單一的存儲單元(INT,炭,等等)。所以即使使用編譯指示,我也希望這兩個位字段佔用2個字符或整數。我沒有該編譯器的文檔 - 它是否說該編譯指示如何影響位域?

1

#pragma pack指令是正確的,但我認爲基本類型的bifields(verlenflagfrag)是int,而不是charshort您所期望。

0

unsigned相當於這裏unsigned int,這是4個字節。所以iplen應該在偏移量23,這聽起來像是。