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

问题描述 投票:0回答:2

我的方案:我有一个异步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

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

任何人都可以帮助我什么可能导致有时我只得到一部分信息不是全部?

c# .net json tcp
2个回答
0
投票

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

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

这样做的典型解决方案是:

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

0
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.