Binary Packet Overview/Tutorial on the Wiki

Started by
5 comments, last by MasterEvilAce 14 years, 7 months ago
I took the time to rewrite my binary packet article. I find bit packets interesting to work with and since formatting packets comes up once in a while this article might help people understand how a bit packet can be used with an intuitive interface. Binary Packet I decided to leave out the disadvantages. Hplus brought up a good point. Packet sniffers are not going to like this at all since rarely is the data byte aligned. The advantages of working on the bit level should offset this though. Oh and if anyone wants to take the time to rewrite it, the C++ version of the code is kind of just for demonstration purposes. I just converted it pretty much one to one from my C# code. Comments are welcome. It's a wiki also and not a real article so anything can be changed. (Or just mention it here and I'll take care of it. Like if something isn't clear or if something is worded badly).
Advertisement
Couple of questions:

1. Wouldn't the bitIndex and maxBitIndex fields need to be set here to avoid overwrites on sequential Write calls?
public BinaryPacket(byte[] buffer){   this.buffer = new List<byte>(buffer);}

2. How does C#'s memory manage features work in relation to the efficiencies of allocating a new byte at runtime vs. using a rather large static buffer that you only allocate one? Is each byte treated as an object that the GC has to track?

3. Just an observation, on the Read functions, I usually found it nicer to be able to setup them in this manner:
public TYPE ReadType(){  if bad    throw an exception  return good value}

That way, if you have larger packets and are reading a bit of data at a time, you can just wrap the entire packet in a try/catch block and if the catch is ever hit, you can assume the packet was malformed, or you messed up somewhere. I've tried using a similar method with the bool and reference parameters in C++, but it felt so unnatural. I believe exceptions generated from packet reading stay true to the purpose of exceptions and a return value of true/false is a misuse in this case since everything "should" parse correctly under normal conditions. If a client sends a bad packet, I'd disconnect it.

Other than that, looks good, thanks for sharing. [smile] I've not worked on the bit level of building and reading packets, so it's interesting to see this code. One thing I have started loving about .net ever since I've started a few weeks ago are the BinaryReader/Writer and MemoryStream classes that allow me to implement Byte packet operations so easily whereas I always had to code them myself in C/C++.
Quote:2. How does C#'s memory manage features work in relation to the efficiencies of allocating a new byte at runtime vs. using a rather large static buffer that you only allocate one? Is each byte treated as an object that the GC has to track?

byte is a value type, which means it is allocated on the stack and is not tracked by the garbage collector.
Mike Popoloski | Journal | SlimDX
Quote:Original post by Drew_Benton
Couple of questions:

1. Wouldn't the bitIndex and maxBitIndex fields need to be set here to avoid overwrites on sequential Write calls?
public BinaryPacket(byte[] buffer){   this.buffer = new List<byte>(buffer);}

I'm not even sure why I have that constructor. I should probably just remove it. I had this idea that someone might try to write corrupt packets to an error log and read them later.
Quote:Original post by Drew_Benton
3. Just an observation, on the Read functions, I usually found it nicer to be able to setup them in this manner:
public TYPE ReadType(){  if bad    throw an exception  return good value}

That way, if you have larger packets and are reading a bit of data at a time, you can just wrap the entire packet in a try/catch block and if the catch is ever hit, you can assume the packet was malformed, or you messed up somewhere. I've tried using a similar method with the bool and reference parameters in C++, but it felt so unnatural. I believe exceptions generated from packet reading stay true to the purpose of exceptions and a return value of true/false is a misuse in this case since everything "should" parse correctly under normal conditions. If a client sends a bad packet, I'd disconnect it.
This point was brought up before. I had it originally where I had exceptions. I might switch it back now that you brought it up.

Also something I didn't mention before and it should be clear from looking at the code. You can only write once. The code can't handle overwrites. I wasn't sure if I should add exceptions to handle the case where someone tries to overwrite data.

Also if you're worried about performance just test it. You'll notice it's extremely fast. (Much faster than my old implementations). I ran some benchmarks a while back for someone, and I'll do some more if someone is curious. (Though packets tend to be small so it's not much of a problem. Doesn't come up in slimtune even with a ton of clients connected to my socket server).
Quote:Original post by Sirisian
Quote:Original post by Drew_Benton
Couple of questions:

1. Wouldn't the bitIndex and maxBitIndex fields need to be set here to avoid overwrites on sequential Write calls?
public BinaryPacket(byte[] buffer){   this.buffer = new List<byte>(buffer);}

I'm not even sure why I have that constructor. I should probably just remove it. I had this idea that someone might try to write corrupt packets to an error log and read them later.

Isn't that constructor required.. or else you can never actually load a byte array from a packet that was generated from a separate BinaryPacket class...

Anyways.. My question is in regards to the implementation of your BinaryPacket and sending this over a NetworkStream. Specifically I am using a TCPClient and ASync read/write (client.GetStream().EndRead(ar))

I understand I can create a binary packet and then call bp.Buffer which creates a byte array and send that.

However I need help on implementing a good OnReceive function. I have a byte[] readBuffer that contains the data received from a NetworkStream. What exactly do I need to do at this point? The BinaryPacket has the length of the packet as the first few bytes, however given that we can't be sure that the packet was split up (and thus not received fully), or that we may have received TWO packets in readBuffer... how do I handle this?

I need a good solution to separate whole packets out of the readBuffer so I can create separate BinaryPackets to send to other functions to process, leaving any 'half packets' in the readBuffer to get appended to

Your class is brilliant
Thanks.
My actual code is a little complex with IOCP and some interesting design decisions so I made some changes for this example, but basically you can look at it like:
// buffer is a byte[] buffer = new buffer[1400]; that you use to read with// Call the receive function someplace and get bufferSize and filled buffer// meh don't ask me why but I like putting it in a temporary reference.BinaryPacket packet = receiveStateObject.Packet;int bufferIndex = 0;while (bufferIndex != bufferSize){    // Do we have the length yet?    if (packet.Length < packet.HeaderSize)    {        for (; packet.Length < packet.HeaderSize && bufferIndex < bufferSize; ++bufferIndex)        {            packet.WriteByte(buffer[bufferIndex]);        }        if (packet.Length == packet.HeaderSize)        {            packet.ReadLength();        }        else        {            break;        }    }    for (; packet.BitIndex < packet.MaxBitIndex && bufferIndex < bufferSize; ++bufferIndex)    {        packet.WriteByte(buffer[bufferIndex]);    }    if (packet.BitIndex >= packet.MaxBitIndex)    {        // Set the bit index so that the first read starts after the 4 byte length header        packet.BitIndex = packet.HeaderSizeInBits;        // Pass off the full complete packet        AsyncPacket(socket, packet);        // Now we need a new packet to build with        receiveStateObject.Packet = new BinaryPacket();        packet = receiveStateObject.Packet;    }}// using IOCP you'd call BeginReceive here again and pass in the receiveStateObject which holds partial packets.
Sirisian, Thanks for the response. Looks great. I've implemented it and it looks like it should work well. Haven't had time to actually test it out, busy with other things.

Your BinaryPacket solution looks to be the easiest method i've found for doing this kind of thing.

This topic is closed to new replies.

Advertisement