我正在练习实现一些基本的第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个字节,所以我可以通过套接字发送。
另外,我目前正在使用整数来表示枚举中的位,是否可以使用位/十六进制或比int更好的东西?例如,模式枚举定义如下:
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, page 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) |
| |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
答案 0 :(得分: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中是否真正表示了哪种位排列。如果您有具有预期值的样本包,那将使事情更加清晰。
答案 1 :(得分:1)
仅供参考,有一个结构代表.NET,System.Boolean
。正如Marc所提到的,协议是偶数字节,所以你可以使用int(每个int保存32位),或者使用bitmask样式的枚举。无论哪种方式,您都可以使用System.BitConverter
的静态方法进行字节数组的转换。
答案 2 :(得分:1)
您是否考虑过使用Flags属性?它允许您将枚举类型值视为位而不是整数: http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx
答案 3 :(得分:1)
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 bits = 1 byte。 并且sizeof(Sratum)= 8位= 1字节。
答案 4 :(得分:1)
序列化:要将数据包字段放入结果中,只需将2(左移)乘以适当的位数,然后将OR与累积结果相乘。
反序列化:要从结果中提取数据包字段,只需使用AND位掩码然后除以2(右移)适当的位数。