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)

2 Answers 2

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);
Sign up to request clarification or add additional context in comments.

4 Comments

Thats better. Didn't cross my mind. Much nicer! I imagine there will be some performance benefits too?
yes. it should be a lot faster since MemoryStream's internal buffer handling is a bit more optimal
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).
@jgauffin: That's exactly what I've just said.
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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.