Получение другого размера заголовка путем изменения размера окна - PullRequest
7 голосов
/ 29 сентября 2008

У меня есть программа на C ++, представляющая заголовок TCP в виде структуры:

#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 байт, правильный размер. Почему это?

Я использую Microsoft Visual Studio 2005 с пакетом обновления 1 на 32-разрядной машине с архитектурой x86.

Ответы [ 9 ]

6 голосов
/ 29 сентября 2008

Поскольку компилятор упаковывает ваше битовое поле в 32-битное целое, а не в 16-битную сущность.

В общем, вы должны избегать битовых полей и использовать другие константы манифеста (перечисления или что-то еще) с явной битовой маскировкой и сдвигом для доступа к «подполям» в поле.

Вот одна из причин, по которой следует избегать битовых полей - они не очень переносимы между компиляторами даже для одной и той же платформы. из стандарта C99 (в стандарте C90 аналогичная формулировка):

Реализация может выделить любой адресная единица хранения достаточно большая держать битовое поле. Если достаточно места остается битовое поле, которое сразу следует за другим битовым полем в структура должна быть упакована в смежные биты одного и того же блока. Если недостаточно места, будь битовое поле не подходит в следующий блок или перекрытия смежные блоки реализации. Получатель чего-то распределение битовых полей в блоке (от высокого до низкого или низкого порядка в высоком порядке) реализации. Выравнивание адресного блока хранения не определено.

Вы не можете гарантировать, будет ли битовое поле «охватывать» границу int, или нет, и вы не можете указать, начинается ли битовое поле в нижнем конце int или в верхнем конце int (это не зависит от того, процессор с прямым порядком байтов или прямым порядком байтов).

4 голосов
/ 29 сентября 2008

Ваша серия битовых полей unsigned int: xx использует только 16 из 32 битов в int. Остальные 16 бит (2 байта) есть, но не используются. За этим следует короткий без знака, который находится на границе int, а затем WORD, который выровнен вдоль границы int, что означает, что между ними есть 2 байта заполнения.

Когда вы переключаетесь на «unsigned int wWindow: 16», вместо того, чтобы быть отдельным коротким, компилятор использует неиспользуемые части предыдущего битового поля, так что без траты, без короткого замыкания и без заполнения после короткого, следовательно, вы сохраняете четыре байта.

2 голосов
/ 29 сентября 2008

См. Этот вопрос: Почему sizeof для структуры не равен сумме sizeof каждого члена? .

Я считаю, что компилятор получает подсказку, чтобы отключить заполнение при использовании синтаксиса unsigned int wWindow: 16.

Также обратите внимание, что короткое замыкание не гарантируется равным 16 битам. Гарантия такова: 16 бит <= размер короткого <= размер целого. </p>

0 голосов
/ 30 сентября 2008

Я думаю, что Майк Б понял это правильно, но не совсем ясно. Когда вы запрашиваете «короткий», он выравнивается по 32-битной границе. Когда вы просите int: 16, это не так. Таким образом, int: 16 помещается сразу после этих полей, в то время как short пропускает 2 байта и начинается со следующего 32-битного блока.

Остальная часть того, что он говорит, совершенно применима - битовое поле никогда не должно использоваться для кодирования внешне видимой структуры, потому что нет никакой гарантии относительно того, как они распределены. В лучшем случае они принадлежат встроенным программам, где важно сохранить байт. И даже там вы не можете использовать их для фактического управления битами в отображаемых в памяти портах.

0 голосов
/ 29 сентября 2008

Вы видите разные значения из-за правил компиляции. Вы можете увидеть правила, специфичные для visual studio здесь .

Если у вас есть структура, которая должна быть упакована (или придерживаться некоторых конкретных требований выравнивания), вы должны использовать опцию #pragma pack (). Для вашего кода вы можете использовать #pragma pack (0), который выровняет все элементы структуры по границам байтов. Затем вы можете использовать #pragma pack () для сброса структуры упаковки в состояние по умолчанию. Вы можете увидеть больше информации о пакете прагма здесь .

0 голосов
/ 29 сентября 2008

Интересно - я думаю, что "WORD" будет оцениваться как "unsigned short", поэтому у вас будет эта проблема в более чем одном месте.

Также помните, что вам нужно иметь дело с проблемами порядка байтов в любом значении более 8 бит.

0 голосов
/ 29 сентября 2008

Не эксперт C / C ++, когда дело доходит до упаковки. Но я полагаю, что в спецификации есть правило, которое гласит, что, когда не битовое поле следует за битовым полем, оно должно быть выровнено по границе слова независимо от того, помещается ли оно в оставшееся пространство. Делая это явным битвектором, вы избегаете этой проблемы.

Опять же, это предположение с оттенком опыта.

0 голосов
/ 29 сентября 2008

Границы структуры в памяти могут дополняться компилятором в зависимости от размера и порядка полей.

0 голосов
/ 29 сентября 2008

Компилятор дополняет элемент структуры без битового поля до 32-битного выравнивания собственных слов. Чтобы это исправить, выполните #pragma pack (0) перед struct и #pragma pack () после.

...