0

I am practising implementing some basic layer 7 protocols but I am unsure of the best way of serialising and deserialising bits in the .Net framework.

According to the MSDN Data Type Summary, there is no bit data type. I have no idea how I would go about creating such a data type or even if it's possible so I am left with serialising/deserialising to a byte / byte array.

Given the following example from the top of an NTP packet:

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

I would like to encode into 2 bytes so I can send via the socket.

Also, I am currently using ints to represent the bits in enums, is it possible to use bits/hex or something a better than ints? For example the mode enum is defined as follows:

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
}

Side Note: The code for this project will eventually be open sourced, please bare in mind that if you answer. If you do not wish for the code to be shared, please say :) A link will be placed in the code back to this question.

Thanks in advance :)

Update: In case people are wondering what the NTP packet structure looks like, taken directly from 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)                         |
        |                                                               |
        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Stuart Blackler
  • 3,732
  • 5
  • 35
  • 60
  • 1
    have you looked into the BitArray class? http://msdn.microsoft.com/en-us/library/system.collections.bitarray(v=vs.90).aspx – RobH Oct 30 '12 at 22:38
  • 1
    `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? ` Please don't opensource it before understanding the basic concepts. – L.B Oct 30 '12 at 22:38
  • 3
    You don't need BitArray for this - just shift operators and "or" (`<<`, `>>` and `|`) – Marc Gravell Oct 30 '12 at 22:39
  • Never knew the BitArray class even existed. Thanks for the link Munchies, ill take a look. I'll have to look up bitwise shifting, is it easy? Thanks Marc :) – Stuart Blackler Oct 30 '12 at 22:41
  • It looks like the protocol is neatly arranged into byte-sized chunks - it should be pretty simple. There are some protocols that genuinely do talk in bits (they aren't aligned) - that gets messier. – Marc Gravell Oct 30 '12 at 22:43
  • @L.B That's kind of why I asked, I come from a self taught background :) It wont be OS'd until it's working then I will accept updates to make the code better which i will learn from in turn :) – Stuart Blackler Oct 30 '12 at 22:46
  • @MarcGravell, just added the packet structure so you can see for yourself. It's pretty nice except for the extension fields. Perfect to practise implementing a protocol :) – Stuart Blackler Oct 30 '12 at 22:49
  • 1
    @StuartBlackler [this code](http://www.dotnet-snippets.com/dns/c-simple-network-time-ntp-protocol-client-SID571.aspx) may help you in implementing your protocol – L.B Oct 30 '12 at 22:57

5 Answers5

3

I don't think I'd use an enum here at all. I'd probably create a struct to represent the packet header, storing the data in a ushort (16 bits):

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; } }
}

You'll want to check this though - it's not immediately clear what bit arrangement is really represented in the RFC. If you have sample packets with expected values, that would make things much clearer.

Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
1

FYI, there is a struct that represents a bit in .NET, it's System.Boolean. As mentioned by Marc, the protocol is in even bytes, so you could use an int (with each int holding 32 bits), or use enums in a bitmask style. Either way, you can use the System.BitConverter's static methods to do convert to and from byte arrays.

Brian Ball
  • 12,268
  • 3
  • 40
  • 51
1

Have you considered using the Flags attribute? It allows you to treat enumerated type values as bits instead of ints: http://msdn.microsoft.com/en-us/library/system.flagsattribute.aspx

  • this is not the case of flags. for example "client" is not the combination: "symmetric active"||"symmetric passive" – Boolean Nov 06 '12 at 12:53
1

The smallest type of an enum in c# is byte (the other types available are explained here http://msdn.microsoft.com/en-us/library/sbbt4032.aspx). Define an enum of type byte:

enum Name:byte{}

in your example:

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
}

If we wish to save space but have less readability, we can see that sizeof(LeapIndicator) + sizeof(VersionNumber) + sizeof(Mode) = 8 bits = 1 byte. and also sizeof(Sratum) = 8 bits = 1 byte.

Boolean
  • 126
  • 5
1

Serialize: To put your packet fields into the result, simply multiply by 2 (left shift) by the appropriate number of bits and then OR with the cumulative result so far.

Deserialize: To extract your packet fields from the result, simple use an AND bitmask then divide by 2 (right shift) by the appropriate number of bits.

Bradley Thomas
  • 4,060
  • 6
  • 33
  • 55