TCP 服务器收到比预期更多的数据

TCP server receiving more data than expected

我有一个服务器-客户端应用程序,客户端将图像数据流式传输到服务器。我有以下结构:

客户:

private void SerializeAndSendMessage(Message msg) {
        BinaryFormatter formatter = new BinaryFormatter();
        MemoryStream stream = new MemoryStream();
        formatter.Serialize(stream, msg);
        byte[] buffer = stream.ToArray();

        if (clientSocket != null)
        {
            if (clientSocket.Connected)
            {
                clientSocket.BeginSend(buffer, 0, buffer.Length, SocketFlags.None, SendCallback, null);
            }
        }
    }

private void SendCallback(IAsyncResult ar) {
        try
        {
            clientSocket.EndSend(ar);
            Debug.WriteLine("Message sent.");
        }
        catch (Exception ex)
        {
            //
        }
    }

服务器:

  private void ReceiveCallback(IAsyncResult ar)
    {
        try
        {
            int received = clientSocket.EndReceive(ar);
            Array.Resize(ref buffer, received);
            BinaryFormatter formatter = new BinaryFormatter();

            MemoryStream stream = new MemoryStream(buffer);

            object obj = null;
            stream.Position = 0;
            try
            {
                obj = formatter.Deserialize(stream);
            }
            catch (Exception ex )
            {
               //
            }

            // processing data

            Array.Resize(ref buffer, clientSocket.ReceiveBufferSize);
            clientSocket.BeginReceive(buffer, 0, buffer.Length, SocketFlags.None, ReceiveCallback, null);
        }
        catch (Exception ex)
        {
            //
        }
    }

我期望发生的事情:

  1. 服务器开始接受来自客户端的数据
  2. 客户端发送大小为 X 的数据
  3. 服务器收到大小为 X 的数据并开始处理它
  4. 客户端还在发送数据
  5. 服务器没有收到此数据
  6. 服务器处理完接收到的数据,现在开始从客户端接收数据
  7. 转到 2

发生了什么:

  1. 服务器开始接受来自客户端的数据
  2. 客户端发送大小为 X 的数据
  3. 服务器收到大小为 X 的数据并开始处理它
  4. 客户端还在发送数据
  5. 服务器没有收到此数据
  6. 服务器处理完接收到的数据,现在开始从客户端接收数据
  7. 客户端发送第 N 个大小为 X 的数据包
  8. 服务器收到大小为M*X的数据

这显然会导致服务器上的缓冲区填满而无法反序列化发送的包。我错过了什么?我该怎么做才能实现上述工作原理?

TCP 是一种流式传输协议。如果您在客户端依次执行多个发送操作,TCP 将在尝试填充 mtu 时将它们组合成一个段。

如果 mtu 已满,或者连续 50 毫秒计时器超时,或者客户端本身必须确认它从服务器收到的数据包,TCP 将发送。

TCP 是一个非常复杂的协议。那里还有一个计算 window 大小的算法。此 window 大小也会影响在客户端接收的段的大小。

底线是因为 TCP 是流式协议,没有通过套接字接收的数据包的概念。您收到任意数量的字节,您必须自己将这些字节附加到某种接收缓冲区,具体取决于您在做什么。如果你想要数据包,那么你必须在你发送的数据前面加上一个长度字段,并在服务器上考虑长度。这当然会使代码复杂化。或者,如果您想保持简单,只需使用 UDP。 UDP 确实支持数据包,如果数据包没有在某处丢失,您发送的内容将在接收方以相同的大小接收。 但 UDP 不可靠,数据包可能会丢失。 TCP 可靠,面向连接,但更复杂。

一般的套接字编程不是初学者的话题。