Представление битов и их сериализация в .Net

Я практикую реализацию некоторых базовых протоколов уровня 7, но я не уверен в лучшем способе сериализации и десериализации битов в среде .Net.

Согласно сводке по типам данных MSDN , нет битового типа данных. Я понятия не имею, как я буду создавать такой тип данных или даже если это возможно, поэтому мне остается сериализация/десериализация в массив байтов/байтов.

Учитывая следующий пример из верхней части пакета NTP:

     0-1         LeapIndicator (LI)      2 bits
     2-4         VersionNumber (VN)      3 bits
     5-7         Mode                    3 bits
     8-15        Stratum                 8 bits

Я хотел бы закодировать в 2 байта, чтобы я мог отправить через сокет.

Кроме того, в настоящее время я использую целые числа для представления битов в перечислениях, возможно ли использовать биты/шестнадцатеричные числа или что-то лучше, чем целые числа? Например, перечисление режимов определяется следующим образом:

public enum Mode
{
    /*
     +-------+--------------------------+
     | Value | Meaning                  |
     +-------+--------------------------+
     | 0     | reserved                 |
     | 1     | symmetric active         |
     | 2     | symmetric passive        |
     | 3     | client                   |
     | 4     | server                   |
     | 5     | broadcast                |
     | 6     | NTP control message      |
     | 7     | reserved for private use |
     +-------+--------------------------+
     */

    Resevered = 0,
    SymmetricActive = 1,
    SymmetricPassive = 2,
    Client = 3,
    Server = 4,
    Broadcast = 5,
    ControlMessage = 6,
    PrivateUse = 7
}

Боковое примечание: код для этого проекта в конечном итоге будет с открытым исходным кодом, имейте это в виду, если вы ответите. Если вы не хотите, чтобы код передавался, скажите, пожалуйста :) В коде будет размещена ссылка на этот вопрос.

Заранее спасибо :)

Обновление: если людям интересно, как выглядит структура пакета NTP, взято непосредственно из RFC 5905, стр. 18.

        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
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |LI | VN  |Mode |    Stratum     |     Poll      |  Precision   |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                         Root Delay                            |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                         Root Dispersion                       |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                          Reference ID                         |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        +                     Reference Timestamp (64)                  +
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        +                      Origin Timestamp (64)                    +
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        +                      Receive Timestamp (64)                   +
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        +                      Transmit Timestamp (64)                  +
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        .                                                               .
        .                    Extension Field 1 (variable)               .
        .                                                               .
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        .                                                               .
        .                    Extension Field 2 (variable)               .
        .                                                               .
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                          Key Identifier                       |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
        |                                                               |
        |                            dgst (128)                         |
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+

person Stuart Blackler    schedule 30.10.2012    source источник
comment
вы изучали класс BitArray? msdn.microsoft.com/en -нас/библиотека/   -  person RobH    schedule 31.10.2012
comment
but I am unsure of the best way of serialising and deserialising bits in the .Net framework. , I am currently using ints to represent the bits in enums, is it possible to use bits/hex or something a better than ints? Пожалуйста, не открывайте исходный код, пока не разберетесь с основными понятиями.   -  person L.B    schedule 31.10.2012
comment
Для этого вам не нужен BitArray - просто операторы сдвига и или (<<, >> и |)   -  person Marc Gravell    schedule 31.10.2012
comment
Никогда не знал, что класс BitArray вообще существует. Спасибо за ссылку, Munchies, посмотрю. Мне придется искать побитовый сдвиг, это легко? Спасибо Марк :)   -  person Stuart Blackler    schedule 31.10.2012
comment
Похоже, что протокол аккуратно разбит на фрагменты размером в байт — он должен быть довольно простым. Есть некоторые протоколы, которые действительно разговаривают по битам (они не выровнены) — это становится еще более запутанным.   -  person Marc Gravell    schedule 31.10.2012
comment
@L.B Вот почему я спросил, я пришел из самоучки :) Он не будет ОС, пока он не заработает, тогда я приму обновления, чтобы сделать код лучше, чему я, в свою очередь, научусь :)   -  person Stuart Blackler    schedule 31.10.2012
comment
@MarcGravell, только что добавил структуру пакета, чтобы вы могли убедиться сами. Это довольно приятно, за исключением полей расширения. Идеально подходит для практики реализации протокола :)   -  person Stuart Blackler    schedule 31.10.2012
comment
@StuartBlackler этот код может помочь вам в реализации вашего протокола   -  person L.B    schedule 31.10.2012


Ответы (5)


Я не думаю, что я бы вообще использовал перечисление здесь. Я бы, вероятно, создал структуру для представления заголовка пакета, сохраняя данные в ushort (16 бит):

public struct NtpHeader
{
    private readonly ushort bits;

    // Creates a header from a portion of a byte array, e.g
    // given a complete packet and the index within it
    public NtpHeader(byte[] data, int index)
    {
        bits = (ushort) (data[index] + (data[index] << 8));
    }

    public NtpHeader(int leapIndicator, int versionNumber,
                     int mode, int stratum)
    {
        // TODO: Validation
        bits = (ushort) (leapIndicator |
                         (versionNumber << 2) |
                         (mode << 5) |
                         (stratum << 8));
    }

    public int LeapIndicator { get { return bits & 3; } }

    public int VersionNumber { get { return (bits >> 2) & 7; } }

    public int Mode { get { return (bits >> 5) & 7; } }

    public int Stratum { get { return bits >> 8; } }
}

Вы захотите проверить это, хотя не сразу понятно, какое расположение битов действительно представлено в RFC. Если у вас есть примеры пакетов с ожидаемыми значениями, это прояснит ситуацию.

person Jon Skeet    schedule 09.11.2012

К вашему сведению, есть структура, представляющая бит в .NET, это System.Boolean. Как упоминал Марк, протокол состоит из четных байтов, поэтому вы можете использовать целое число (каждое целое число содержит 32 бита) или использовать перечисления в стиле битовой маски. В любом случае вы можете использовать статические методы System.BitConverter для преобразования в байтовые массивы и из них.

person Brian Ball    schedule 05.11.2012

Рассматривали ли вы возможность использования атрибута Flags? Он позволяет обрабатывать значения перечисляемого типа как биты, а не как целые числа: http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx

person Michael Johnson    schedule 06.11.2012
comment
это не относится к флагам. например, клиент не является комбинацией: симметричный актив || симметричный пассив - person Boolean; 06.11.2012

Наименьшим типом перечисления в C# является byte (другие доступные типы описаны здесь http://msdn.microsoft.com/en-us/library/sbbt4032.aspx). Определите перечисление типа byte:

enum Name:byte{}

в вашем примере:

public enum Mode:byte
{
    /*
     +-------+--------------------------+
     | Value | Meaning                  |
     +-------+--------------------------+
     | 0     | reserved                 |
     | 1     | symmetric active         |
     | 2     | symmetric passive        |
     | 3     | client                   |
     | 4     | server                   |
     | 5     | broadcast                |
     | 6     | NTP control message      |
     | 7     | reserved for private use |
     +-------+--------------------------+
     */

    Resevered = 0,
    SymmetricActive = 1,
    SymmetricPassive = 2,
    Client = 3,
    Server = 4,
    Broadcast = 5,
    ControlMessage = 6,
    PrivateUse = 7
}

Если мы хотим сэкономить место, но менее удобочитаемы, мы можем увидеть, что sizeof(LeapIndicator) + sizeof(VersionNumber) + sizeof(Mode) = 8 бит = 1 байт. а также sizeof(Sratum) = 8 бит = 1 байт.

person Boolean    schedule 08.11.2012

Сериализация: чтобы поместить поля вашего пакета в результат, просто умножьте на 2 (сдвиг влево) на соответствующее количество бит, а затем выполните операцию ИЛИ с совокупным результатом на данный момент.

Десериализация: чтобы извлечь поля пакета из результата, просто используйте битовую маску И, а затем разделите на 2 (правый сдвиг) на соответствующее количество битов.

person Brad Thomas    schedule 08.11.2012
comment
Или просто используйте операторы побитового сдвига ›› и ‹‹. - person redcalx; 09.11.2012