0

Currently in the process of writing some TCP socket code and running into a small issue.

Basically where I am confused is the following couple of lines of code.

NetworkStream clientStream = tcpClient.GetStream();
List<Byte> fullMessage = new List<Byte>();

Byte[] message = new Byte[4096];
Byte[] currentMessage = new Byte[4096];
Int32 bytesRead = 0;

if (clientStream.CanRead)
{
    do
    {
        bytesRead = clientStream.Read(message, 0, 4096);

        Array.Resize<Byte>(ref currentMessage, bytesRead);
        Array.Copy(message, currentMessage, bytesRead);

        fullMessage.AddRange(currentMessage);

    } while (clientStream.DataAvailable);
}

Specifically regarding the best way to handle the fact even though the message byte array is declared at 4096 bytes the amount of data retrieved is arbitrary and cannot be computed.

So is the way I am handling the response considered a reasonable solution or is there a better way? (IE: Creating a new sized array based on the bytesRead value)

Maxim Gershkovich
  • 45,951
  • 44
  • 147
  • 243

2 Answers2

1

If you know that the message won't exceed 4096 bytes, then you can write something like this:

int totalBytesRead = 0;
int bytesRead;
while ((bytesRead = clientStream.Read(message, totalBytesRead, message.Length - totalBytesRead)) !=0)
{
    totalBytesRead += bytesRead;
}

totalBytesRead is used to tell clientStream.Read where to put the data that it copies.

clientStream.Read returns 0 if there is no data available.

Note that with this setup, you can't read more than message.Length bytes. If your packets can be larger, then I suggest making your buffer bigger. I wouldn't recommend continually resizing the array because that will end up fragmenting the large object heap (if messages become larger than 80 KB), and at some point you have to set a maximum on the size a message can be--even if you handle "arbitrarily large" messages.

Jim Mischel
  • 131,090
  • 20
  • 188
  • 351
1

Use a memory stream:

NetworkStream clientStream = tcpClient.GetStream();
MemoryStream messageStream = new MemoryStream();
byte[] inbuffer = new byte[65535];

if (clientStream.CanRead)
{
    do
    {
        var bytesRead = clientStream.Read(inbuffer, 0, buffer.Length);
        messageStream.Write(inbuffer, 0, bytesRead);
    } while (clientStream.DataAvailable);
}

messageStream.Position = 0;
var completeMessage = new byte[messageStream.Length];
messageStream.Write(completeMessage, 0, messageStream.Length);
jgauffin
  • 99,844
  • 45
  • 235
  • 372
  • Thats better. Didn't cross my mind. Much nicer! I imagine there will be some performance benefits too? – Maxim Gershkovich Apr 18 '11 at 09:20
  • yes. it should be a lot faster since MemoryStream's internal buffer handling is a bit more optimal – jgauffin Apr 18 '11 at 09:26
  • Great example, but it isn't reliable with data that spans multiple packets. The problem is that the loop can iterate faster than data is received which results in clientStream.DataAvailable incorrectly equating to false when more data is still actually on the way. There are multiple ways around this: implement a tiny sleep in each loop (yuck), implement known 'end of stream' bytes (still yuck), or allocate the first four bytes of the stream to a uint value that indicates the size of the payload and stop looping once all bytes are received (and wait for more to be received if necessary). – Bosco Aug 29 '13 at 06:48
  • @jgauffin: That's exactly what I've just said. – Bosco Aug 29 '13 at 20:40