0

我正在练习实现一些基本的第 7 层协议,但我不确定在 .Net 框架中序列化和反序列化位的最佳方式。

根据MSDN Data Type Summary,没有位数据类型。我不知道如何创建这样的数据类型,或者即使有可能,所以我只剩下序列化/反序列化为字节/字节数组。

给定以下来自 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)                         |
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
4

5 回答 5

3

我认为我根本不会在这里使用枚举。我可能会创建一个结构来表示数据包头,将数据存储在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 中真正表示的位排列。如果您有具有预期值的样本数据包,那会让事情变得更加清晰。

于 2012-11-09T07:01:53.580 回答
1

仅供参考,在 .NET 中有一个代表位的结构,它是System.Boolean. 正如 Marc 所提到的,该协议以偶数字节为单位,因此您可以使用 int(每个 int 保存 32 位),或使用位掩码样式的枚举。无论哪种方式,您都可以使用System.BitConverter的静态方法与字节数组进行转换。

于 2012-11-05T23:33:10.150 回答
1

您是否考虑过使用 Flags 属性?它允许您将枚举类型值视为位而不是整数:http: //msdn.microsoft.com/en-us/library/system.flagsattribute.aspx

于 2012-11-06T02:25:50.883 回答
1

c# 中枚举的最小类型是字节(其他可用类型在此处解释http://msdn.microsoft.com/en-us/library/sbbt4032.aspx)。定义一个字节类型的枚举:

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 bits = 1 byte。还有 sizeof(Sratum) = 8 bits = 1 byte。

于 2012-11-08T07:26:05.593 回答
1

序列化:要将您的数据包字段放入结果中,只需将 2(左移)乘以适当的位数,然后与到目前为止的累积结果相乘。

反序列化:要从结果中提取数据包字段,只需使用 AND 位掩码,然后除以 2(右移)除以适当的位数。

于 2012-11-08T17:52:49.103 回答