Получение другого размера заголовка путем изменения размера окна

StackOverflow https://stackoverflow.com/questions/149995

  •  02-07-2019
  •  | 
  •  

Вопрос

У меня есть программа на 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.

Это было полезно?

Решение

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

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

Также обратите внимание, что короткое замыкание не гарантировано равным 16 битам.Гарантия заключается в том, что:16 бит <= размер короткого <= размер int.

Другие советы

Потому что компилятор упаковывает ваше битовое поле в 32-разрядный int, а не в 16-разрядный объект.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top