3
votes

I have a client-server application where the server transmits a 4-byte integer specifying how large the next transmission is going to be. When I read the 4-byte integer on the client side (specifying FILE_SIZE), the next time I read the stream I get FILE_SIZE + 4 bytes read.

Do I need to specify the offset to 4 when reading from this stream, or is there a way to automatically advance the NetworkStream so my offset can always be 0?

SERVER

NetworkStream theStream = theClient.getStream();

//...
//Calculate file size with FileInfo and put into byte[] size
//...

theStream.Write(size, 0, size.Length);
theStream.Flush();

CLIENT

NetworkStream theStream = theClient.getStream();

//read size
byte[] size = new byte[4];
int bytesRead = theStream.Read(size, 0, 4);

...

//read content
byte[] content = new byte[4096];
bytesRead = theStream.Read(content, 0, 4096);

Console.WriteLine(bytesRead); // <-- Prints filesize + 4 
2
What is filesize? Is it the same as size.Length? - Mark Byers
Are you certain that size.Length (server-side) is 4? (And that you're not reading the next file's size in that second read on the client side?) - Mat
What is size? What you're doing will be unstable. Use BinaryReader/BinaryWriter instead with ReadInt32/WriteInt32 to avoid sizing issues. - Ivan G.
NetworkStream does not support random access to the network data stream. The value of the CanSeek property, which indicates whether the stream supports seeking, is always false; reading the Position property, reading the Length property, or calling the Seek method will throw a NotSupportedException. - From MSDN. Perhaps you can read the stream and discard the first 4 bytes when reading the content? - Tim
size is a byte[] obtained via BitConverter.GetBytes(fileInfo.Length); where fileInfo = new FileInfo(myFileName); - jkh

2 Answers

6
votes

Right; found it; FileInfo.Length is a long; your call to:

binWrite.Write(fileInfo.Length);

writes 8 bytes, little-endian. You then read that back via:

filesize = binRead.ReadInt32();

which little-endian will give you the same value (for 32 bits, at least). You have 4 00 bytes left unused in the stream, though (from the high-bytes of the long) - hence the 4 byte mismatch.

Use one of:

  • binWrite.Write((int)fileInfo.Length);
  • filesize = binRead.ReadInt64();
1
votes

NetworkStream certainly advances, but in both cases, your read is unreliable; a classic "read known amount of content" would be:

static void ReadAll(Stream source, byte[] buffer, int bytes) {
  if(bytes > buffer.Length) throw new ArgumentOutOfRangeException("bytes");
  int bytesRead, offset = 0;
  while(bytes > 0 && (bytesRead = source.Reader(buffer, offset, bytes)) > 0) {
    offset += bytesRead;
    bytes -= bytesRead;
  }
  if(bytes != 0) throw new EndOfStreamException();
}

with:

ReadAll(theStream, size, 4);
...
ReadAll(theStream, content, contentLength);

note also that you need to be careful with endianness when parsing the length-prefix.

I suspect you simply aren't reading the complete data.