Tcp客户端有时只获取部分消息

时间:2017-12-30 00:10:52

标签: c# .net json tcp

我的情景: 我有一个异步tcp服务器接受连接,并在请求时使用此方法响应:

     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);
        }
    }

我有一个客户端应用程序,它使用这种方法接收数据:

    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();
    }

主要是我发送使用JSON 序列化的对象,并且它一直运行良好。 当我重新启动客户端时,它从服务器获取数据,但有时只有部分数据到达,应用程序崩溃,在JSON反序列化时 ArgumentException崩溃。 当它发生时(随机)是奇怪的 sr.Peek()= -1但clientStream.DataAvailable = true

另一个信息是,它总是崩溃时,readed消息是相同的。

任何人都可以帮助我什么可能导致我有时只收到部分信息?

2 个答案:

答案 0 :(得分:0)

您无法依靠单次接收获取所有已发送的数据,您必须检查收到的字节数并检查它是否是完整的消息。

答案 1 :(得分:0)

TCP API在两个方向上暴露双向字节流 - 而不是数据包API。您在一个套接字上写入流并在另一端读取它。但是,无法保证单个写入只会导致一个或多个读取。这是因为发送的字节可能会在多个IP数据包上分段,有些可能会延迟到达。

如果您想在应用程序层上使用数据包或消息,则需要引入自己的框架机制。

执行此操作的典型解决方案是:

  • 使用它的长度为每条消息添加前缀,该长度以众所周知的格式(例如,4byte大端序号)序列化。然后接收方可以等待长度,然后准确读取该字节数并将其解释为消息。
  • 使用一些分隔符来分隔邮件。例如。在最简单的情况下换行。但是这样做的缺点是,您的消息有效负载中可能不包含此字符(必须进行转义),并且接收方需要搜索它。然而,这是例如HTTP中用于分隔标题和正文部分的机制。