Skip to content

Default framer doesn't handle multiple messages in a buffer #1

@Squiglygon

Description

@Squiglygon

There's a bug in the current read loop in both SimpleAsyncServer and SimpleAsyncClient:

while (!_cancellation.Token.IsCancellationRequested)
{
    var buffer = new byte[_bufferSize];
    var bytesRead = await netStream.ReadAsync(buffer, 0, buffer.Length);
    bool messageReceived = framer.DataReceived(buffer);
    if (messageReceived)
    {
        if (OnMessageReceived != null)
            OnMessageReceived.Invoke(this, new SACMessageReceivedEventArgs(framer.GetMessage(), framer.GetMessage().Length));
    }
}

Consider the following scenario:

  1. Message sizes are 250 bytes, server is writing a burst of messages.
  2. first time netStream.ReadAsync starts, it read a buffer, buffer contains 2600 bytes (hence 10 messages and some bytes from an uncompleted message)
  3. the framer will only extract the first message (placing it in the previousDataBuffer so the framer.getMessage() will fetch it)
  4. the loop will then continue, reading new buffer from netStream, passing it to the framer.
    at this point, few things will happen:
  5. The rest of the 9 messages previously read are gone
  6. the lengthBuffer will read some random 4 bytes, giving a bogus data size (either huge or negative) causing the framer to fail, and the entire stream to be unrecoverable.

I worked around this by adding a readMessage method, but this is just a quick patch, the better approach is to of course solve this within the framer (keeping with the current (good) architecture.

My temporary work around:
(Do note I didn't use wait/async for readSize and readMessage, since they will block anyhow, I don't see the point in that, but you can very well do so.

                using (NetworkStream netStream = _tcpClient.GetStream())
                {
                    while (!_cancellation.Token.IsCancellationRequested)
                    {
                        byte[] message = ReadMessage(netStream);
                        OnMessageReceived.Invoke(this, new SACMessageReceivedEventArgs(message, message.Length));
                    }
                }


        private byte[] ReadMessage(NetworkStream netStream)
        {
            int msgSize = ReadSize(netStream);
            byte[] message = new byte[msgSize];

            int read = 0;
            while (read < msgSize)
            {
                read += netStream.Read(message, read, msgSize - read);
            }
            return message;
        }

        private int ReadSize(NetworkStream netStream)
        {
            byte[] sizeBuffer = new byte[sizeof(int)];
            int read = 0;
            while (read < sizeBuffer.Length)
                read += netStream.Read(sizeBuffer, read, sizeBuffer.Length - read);

            return BitConverter.ToInt32(sizeBuffer, 0);
        }

Metadata

Metadata

Assignees

Labels

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions