1
votes

I'm trying to make a function which takes a Stream(any type of stream, NetworkStream,FileStream, etc...) and write a byte array then reads the array, for this case I'm using BinaryWriter and BinaryReader. The writing part works fine, I simply the BinaryWriter.Write function. but the reader part seems to raise an issue, especially when I use a NetworkStream. I couldn't find a way to read all the bytes from stream. so I was wondering if it's even possible to read all bytes from a NetworkStream while treating it as an object of type Stream. I could cast it to a socket then work with socket functions but I wonder if there's a way to write code that can work both with NetworkStream and other type of streams. these are the two functions: Issue is inside first case, in the Deserialize method

        public static void Serialize(Stream stream, object obj)
        {
            BinaryWriter writer = new BinaryWriter(stream);
            switch (Compression)
            {
                case FormatCompression.Binary:
                    writer.Write(BinaryDeconstructor.Deconstruct(obj));
                    break;
                case FormatCompression.String:
                    writer.Write(Deconstructor.Deconstruct(obj));
                    break;
                default:
                    throw new SerializationException("Please choose a format compression");
            }
            writer.Flush();
        }

        public static object Deserialize(Stream stream)
        {
            BinaryReader reader = new BinaryReader(stream);
            switch (Compression)
            {
                case FormatCompression.Binary:
                    byte[] packet = /*HOW?!*/;
                    object obj = BinaryConstructor.Construct(packet);
                    return obj;
                case FormatCompression.String:
                    //this case works fine
                    string objGraphData = reader.ReadString();
                    return Constructor.Construct(objGraphData);
                default:
                    throw new SerializationException("Please choose a format compression");
            }
        }
1

1 Answers

1
votes

The problem with the network stream is that you don't know when to stop reading and start deserializing. That's easy to fix, though: if you pass the number of bytes to read ahead of serializing the array, the reader could read the number first, because the number occupies a fixed number of bytes, and then use that number to read the actual bytes of the array.

Figuring out the number of bytes gets a little tricky. It can be solved by serializing into a temporary memory buffer, harvesting the buffer size, sending the size, and finally send the actual array of bytes, which you now have in memory:

// Writing
var writer = new BinaryWriter(stream);
switch (Compression) {
    case FormatCompression.Binary:
        var memStream = new MemorySteam();
        var memWriter = new BinaryWriter(memStream);
        memWriter.Write(BinaryDeconstructor.Deconstruct(obj));
        memWriter.Flush();
        writer.Write(memStream.Length);
        writer.Write(memStream.GetBuffer());
        break;
    case FormatCompression.String:
        writer.Write(Deconstructor.Deconstruct(obj));
        break;
    default:
        throw new SerializationException("Please choose a format compression");
}
writer.Flush();

Now your binary deserialization code has access to an int that represents the parameter that you pass to the method that reads a specified number of bytes from your stream of any kind. Note that if you would like to use this code in a production situation, you must validate the length that comes off the wire to avoid an attacker passing a too large or a negative number to your program through this protocol.