Skip to Content
Course content
Click on the "Edit" button in the top corner of the screen to edit your slide content.

I've seen this exact bug pop up in almost every junior dev's first attempt at handling binary data. You're working with a MemoryStream, you write some data to it, and then you immediately try to read that data back—only to find that the reader returns nothing, or worse, throws an EndOfStreamException.

public byte[] GetPacketData()
{
    using (var ms = new MemoryStream())
    {
        using (var writer = new BinaryWriter(ms))
        {
            writer.Write(101); // Packet ID
            writer.Write("Hello"); // Payload
        }

        // We want to return the bytes we just wrote
        using (var reader = new BinaryReader(ms))
        {
            // This will throw EndOfStreamException!
            int id = reader.ReadInt32(); 
            return ms.ToArray();
        }
    }
}

The Empty Read Mystery

If you look at the code above, it seems logically sound. You opened a stream, wrote an integer and a string, and then tried to read them. So why does it crash?

Here is the thing about streams: they aren't just buckets of data; they have a position. Think of it like a cassette tape or a record player needle. When you wrote the Packet ID and the payload, the "needle" moved forward with every byte written. By the time the BinaryWriter finished, the position was at the very end of the stream.

When the BinaryReader starts, it doesn't automatically jump back to the beginning. It starts exactly where the writer left off—at the end. It looks forward, sees there's nothing left to read, and panics.

Resetting the Stream Pointer

The fix is simple, but it's the one thing everyone forgets. You have to manually move the position back to zero before you start reading. I usually do this explicitly right after the writer is disposed.

public byte[] GetPacketData()
{
    using (var ms = new MemoryStream())
    {
        using (var writer = new BinaryWriter(ms))
        {
            writer.Write(101);
            writer.Write("Hello");
        }

        // This is the magic line. Reset the needle to the start.
        ms.Position = 0; 

        using (var reader = new BinaryReader(ms))
        {
            int id = reader.ReadInt32(); // Now this works!
            return ms.ToArray();
        }
    }
}

Now, you might be wondering if you can just use ms.ToArray() and skip the reader entirely. You can, but MemoryStream is incredibly powerful when you need to build a complex binary object in memory before sending it over a network or saving it to a file. It avoids the overhead of creating multiple temporary byte arrays and concatenating them, which is a nightmare for the Garbage Collector.

One last pro tip: be careful with ms.GetBuffer(). It returns the internal array used by the stream, which is often larger than the actual data you wrote. If you need exactly the bytes you wrote and nothing more, stick with ToArray(), even though it creates a copy. It'll save you from a lot of "where did these extra zeros come from?" debugging sessions.




📋 Practical Task

Exercise: Building a Binary Message Packetizer

Your task is to create a class called MessagePacketizer that mimics a simple network protocol. The protocol requires that every message starts with a 4-byte integer representing the length of the message, followed by the message string itself in UTF-8 encoding.

Implement a method byte[] CreatePacket(string message) that:

  • Uses a MemoryStream and BinaryWriter to write the length of the string (in bytes) as an Int32.
  • Writes the actual string bytes to the stream.
  • Resets the stream position to 0.
  • Uses a BinaryReader to read the length back first, then the string, just to verify the data is correct before returning the final ToArray() result.

Test your code by passing the string "C# MemoryStreams are powerful!" and ensuring the resulting byte array starts with the correct length integer.

Rating
0 0

There are no comments for now.

to be the first to leave a comment.