【问题标题】:Getting different header size by changing window size通过更改窗口大小获得不同的标题大小
【发布时间】:2010-09-14 01:56:22
【问题描述】:

我有一个将 TCP 标头表示为结构的 C++ 程序:

#include "stdafx.h"

/*  TCP HEADER

    0                   1                   2                   3   
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |          Source Port          |       Destination Port        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                        Sequence Number                        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Acknowledgment Number                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |  Data |           |U|A|P|R|S|F|                               |
   | Offset| Reserved  |R|C|S|S|Y|I|            Window             |
   |       |           |G|K|H|T|N|N|                               |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |           Checksum            |         Urgent Pointer        |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                    Options                    |    Padding    |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
   |                             data                              |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

*/

typedef struct {        // RFC793
    WORD         wSourcePort;
    WORD         wDestPort;
    DWORD        dwSequence;
    DWORD        dwAcknowledgment;
    unsigned int byReserved1:4;
    unsigned int byDataOffset:4;
    unsigned int fFIN:1;
    unsigned int fSYN:1;
    unsigned int fRST:1;
    unsigned int fPSH:1;
    unsigned int fACK:1;
    unsigned int fURG:1;
    unsigned int byReserved2:2;
    unsigned short wWindow;
    WORD         wChecksum;
    WORD         wUrgentPointer;
} TCP_HEADER, *PTCP_HEADER;


int _tmain(int argc, _TCHAR* argv[])
{
    printf("TCP header length: %d\n", sizeof(TCP_HEADER));
    return 0;
}

如果我运行这个程序,我会得到这个标头的大小为 24 字节,这不是我期望的大小。如果我将字段“wWindow”的类型更改为“unsigned int wWindow:16”,它的位数与 unsigned short 相同,程序会告诉我结构的大小现在是 20 字节,这是正确的大小。这是为什么呢?

我在 32 位 x86 机器上使用带有 SP1 的 Microsoft Visual Studio 2005。

【问题讨论】:

    标签: c++ c struct packing


    【解决方案1】:

    由于编译器打包规则,您会看到不同的值。您可以查看特定于 Visual Studio here 的规则。

    当您有一个必须打包的结构(或遵守某些特定的对齐要求)时,您应该使用#pragma pack() 选项。对于您的代码,您可以使用 #pragma pack(0) 来对齐字节边界上的所有结构成员。然后,您可以使用 #pragma pack() 将结构打包重置为其默认状态。您可以在 pack pragma here 上查看更多信息。

    【讨论】:

      【解决方案2】:

      我认为 Mike B 说得对,但不是很清楚。当您要求“短”时,它在 32 位边界上对齐。当您要求 int:16 时,它不是。所以 int:16 正好在 ebit 字段之后,而 short 跳过 2 个字节并从下一个 32 位块开始。

      他所说的其余部分完全适用 - 位字段绝不能用于编码外部可见的结构,因为无法保证它们是如何分配的。充其量,它们属于保存字节很重要的嵌入式程序。即使在那里,您也不能使用它们来实际控制内存映射端口中的位。

      【讨论】:

        【解决方案3】:

        您的“unsigned int:xx”系列位域仅使用了 int 中 32 位中的 16 位。其他 16 位(2 个字节)在那里,但未使用。紧随其后的是 unsigned short,它位于 int 边界上,然后是一个 WORD,它在 int 边界上对齐,这意味着它们之间有 2 个字节的填充。

        当您切换到“unsigned int wWindow:16”时,编译器不会使用单独的short,而是使用前一个位域的未使用部分,因此不会浪费,没有short,并且在short之后没有填充,因此您可以保存四个字节。

        【讨论】:

          【解决方案4】:

          因为编译器将您的位域打包成 32 位 int,而不是 16 位实体。

          一般来说,您应该避免使用位域并使用其他带有显式位掩码和移位的清单常量(枚举或其他)来访问域中的“子域”。

          这是应避免使用位域的一个原因 - 即使对于同一平台,它们在编译器之间的移植性也不是很好。来自 C99 标准(C90 标准中有类似的措辞):

          一个实现可以分配任何 足够大的可寻址存储单元 保存一个位域。如果空间足够 仍然是一个位域,立即 在 a 中跟随另一个位域 结构应装入 同一单元的相邻位。如果 剩余空间不足,无论是 放置不适合的位域 进入下一个单元或重叠 相邻单位是 实现定义。的顺序 单元内位域的分配 (高阶到低阶或低阶 到高阶)是 实现定义。对齐方式 可寻址存储单元的 未指定。

          您无法保证位域是否会“跨越”一个 int 边界,并且您无法指定位域是从 int 的低端还是 int 的高端开始(这与是否处理器是大端或小端)。

          【讨论】:

          • 我想知道是否有人会定义一种可移植的方式来指定位域,至少在与 POSIX 兼容的硬件上,例如使用类似“uInt32_t thing1,thing2;field1:thing1.28.4;field2:thing1.0.28;field3:thing2.12.20;”的语法等等。?这样的字段声明不会分配空间,但会访问先前分配的字段。我已经看到编译器具有在静态声明的变量上覆盖位标志的命名法;我想知道是否有在其他结构字段上覆盖位字段的语法?
          【解决方案5】:

          有趣 - 我认为“WORD”会评估为“unsigned short”,所以你会在不止一个地方遇到这个问题。

          另外请注意,您需要处理任何超过 8 位的值的字节序问题。

          【讨论】:

            【解决方案6】:

            看到这个问题:Why isn't sizeof for a struct equal to the sum of sizeof of each member?

            我相信当您使用“unsigned int wWindow:16”语法时,编译器会提示禁用填充。

            另外,请注意,short 不保证为 16 位。保证是:16 位

            【讨论】:

            【解决方案7】:

            在打包方面不是 C/C++ 专家。但我想规范中有一条规则说,当非位域跟随位域时,无论它是否适合剩余空间,它都必须在字边界上对齐。通过将其设为显式位向量,您可以避免此问题。

            这又是带有一点经验的推测。

            【讨论】:

              【解决方案8】:

              编译器可以根据字段的大小和顺序填充内存中的结构边界。

              【讨论】:

                【解决方案9】:

                编译器将非位域结构成员填充为 32 位 - 本机字对齐。要解决此问题,请在结构之前执行 #pragma pack(0),在之后执行 #pragma pack()。

                【讨论】:

                • #pragma pack (0) 没有改变行为。
                猜你喜欢
                • 2013-09-17
                • 1970-01-01
                • 1970-01-01
                • 2014-01-31
                • 1970-01-01
                • 2015-12-01
                • 2016-06-08
                • 2022-07-08
                • 2012-04-03
                相关资源
                最近更新 更多