0

My scenario: I have an async tcp server which accepts connections,and upon requests it responses with this method:

     private void SendMessage(Client client, string message)
     {
        var buffer = Encoding.UTF8.GetBytes(message);
        try
        {
            client.TCPClient.GetStream().Write(buffer, 0, buffer.Length);
        }
        catch (Exception ex) when (ex is InvalidOperationException || ex is System.IO.IOException)
        {
            RemoveClient(client);
            Task exceptionHandler = Program.ExceptionHandler.Server(ex);
        }
    }

And I have a client application which receives data with this method:

    private string GetMessage()
    {
        StringBuilder returndata = new StringBuilder();
        NetworkStream clientStream = this.tcpClient.GetStream();
        var sr = new StreamReader(clientStream, Encoding.UTF8);
        int value;
        while (sr.Peek() >= 0 || clientStream.DataAvailable)
        {
            value = sr.Read();
            returndata.Append((char)value);
        }
        return returndata.ToString();
    }

Mostly I send objects serialized with JSON,and it has worked fine until now. When I restart the client it grabs data from the server, but sometimes only a part of it arrives and the application crashes with ArgumentException at JSON deserialize. It's strange that when it happens (randomly) sr.Peek() = -1 but clientStream.DataAvailable = true

Another information is that always when it crashes the readed message is the same.

Anyone could help me what could cause that sometimes I got only a part of the message not all?

  • One write does not equal one read. See last example [here](https://msdn.microsoft.com/en-us/library/system.net.sockets.socket(v=vs.110).aspx) for an example. If you search on msdn, there are more examples. – CodingYoshi Dec 30 '17 at 00:49
  • Related: [Broken TCP messages](https://stackoverflow.com/q/7257139). – dbc Dec 30 '17 at 02:21

2 Answers2

1

The TCP APIs expose a bidirectional byte stream in both directions - not a packet API. You write to the stream on one socket and read from it on the other end. However there are no guarantees that a single write will cause exactly one read or more of them. This is because the bytes that are sent might be fragmented over multiple IP packets and some may arrive delayed.

If you want packets or messages on your application layer, you need to introduce your own framing mechanism.

Typical solutions to do this are:

  • Prefix each message with it's length, which is serialized in a well-known format (e.g. 4byte big endian number). Then the receiving side can wait for the length, and afterwards read exactly that number of bytes and interpret it as a message.
  • Use some delimiter characters to separate messages. E.g. a newline in the simplest case. This however has the drawback that this character may not be contained in your message payload (must be escaped) and that the receiver needs to search for it. However this is e.g. the mechanism which is used in HTTP to separate the header and body part.
Matthias247
  • 9,836
  • 1
  • 20
  • 29
0

You can't count on a single receive getting all the data that was sent, you have to check the number of bytes received and check whether it is a complete message.

SoronelHaetir
  • 14,104
  • 1
  • 12
  • 23
  • Ok, I think you are right, but how can I solve that in this situation? It would be nice if you give some advice or link, anything :) – user8932376 Dec 30 '17 at 00:35