通过更改窗口大小获得不同的标题大小

时间:2020-03-06 14:53:19  来源:igfitidea点击:

我有一个将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",其位数与无符号short相同,则程序会告诉我该结构的大小现在为20个字节,即正确的大小。为什么是这样?

我在32位x86计算机上使用带有SP1的Microsoft Visual Studio 2005.

解决方案

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

通常,我们应避免使用位域,而应使用其他清单常量(枚举或者任何其他形式)进行显式位屏蔽和移位,以访问字段中的"子字段"。

这是为什么要避免使用位域的原因之一,即使对于同一平台,位域在编译器之间也不是很容易移植。从C99标准(在C90标准中有类似的措词):

An implementation may allocate any
  addressable storage unit large enough
  to hold a bitfield. If enough space
  remains, a bit-field that immediately
  follows another bit-field in a
  structure shall be packed into
  adjacent bits of the same unit. If
  insufficient space remains, whether a
  bit-field that does not fit is put
  into the next unit or overlaps
  adjacent units is
  implementation-defined. The order of
  allocation of bit-fields within a unit
  (high-order to low-order or low-order
  to high-order) is
  implementation-defined. The alignment
  of the addressable storage unit is
  unspecified.

我们无法保证位字段是否会"跨越"一个int边界,也无法指定位字段是从int的低端还是int的高端开始(这与处理器是否为整数无关)。大端或者小端)。

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

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

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

同样,这是一种带有经验的猜测。

看到以下问题:为什么结构的sizeof不等于每个成员的sizeof之和? 。

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

另外,请注意,不能保证short为16位。保证是:16位<=短的大小<=整数的大小。

有趣的是,我认为" WORD"的取值为" unsigned short",因此我们可能会在多个地方遇到这个问题。

另外请注意,我们需要以8位以上的任何值处理字节序问题。

由于编译器打包规则,我们会看到不同的值。我们可以在此处查看特定于Visual Studio的规则。

当我们具有必须打包的结构(或者遵守某些特定的对齐要求)时,应使用#pragma pack()选项。对于代码,可以使用#pragma pack(0),它将在字节边界上对齐所有结构成员。然后,我们可以使用#pragma pack()将结构打包重置为默认状态。我们可以在此处查看有关打包实用程序的更多信息。

"无符号int:xx"位域系列仅使用int的32位中的16位。其他16位(2个字节)在那里,但未使用。然后是int边界上的无符号short,然后是int边界上对齐的WORD,这意味着它们之间有2个字节的填充。

当我们切换到" unsigned int wWindow:16"时,编译器使用的是前一个位域的未使用部分,而不是单独的short,因此,没有浪费,没有short且在short之后没有填充,因此节省了四个字节。

我认为Mike B做对了,但不是很清楚。当我们要求" short"时,它在32位边界对齐。当我们要求int:16时,不是。所以int:16紧跟在ebit字段之后,而short跳过2个字节并从下一个32位块开始。

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