【问题标题】:C# Socket.Receive() malfunctions when TCP package is larger than MTU当 TCP 包大于 MTU 时 C# Socket.Receive() 发生故障
【发布时间】:2016-06-25 17:15:09
【问题描述】:

我正在使用 C# Socket 类通过短连接向/从我们的服务器发送/接收 TCP 消息。伪代码(排除一些容错逻辑以使其清晰)如下。

class MyTCPClient {
    private Socket mSocket;

    MyTCPClient() {
        mSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
    }

    void Connect(ipEndpoint, sendTimeOut, receiveTimeOut) {
        mSocket.Connect(ipEndpoint);
        mSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.SendTimeout, sendTimeOut);
        mSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReceiveTimeout, receiveTimeOut);
    }

    void Send(msg) {
        mSocket.Send(msg);
    }

    void Receive(data) {
        ByteStreamWriter bsw = new ByteStreamWriter();
        int totalLen = 0;
        while (true) {
            byte[] temp = new byte[1024];
            int len = mSocket.Receive(temp, SocketFlags.None);
            System.Threading.Thread.Sleep(50);  // This is the weirdest part
            if (len <= 0) break;
            totalLen += len;
            bsw.WriteBytes(temp);
        }
        byte[] buff = bsw.GetBuffer();
        data = new byte[totalLen];
        Array.Copy(buff, 0, data, 0, totalLen);
    }

    ~MyTCPClient() {
        mSocket.Close();
    }
}

我使用这个类通过短连接多次向我们的服务器请求相同的消息,并且发生了以下情况(仅当消息大小大于一个 MTU -- 1500 字节时)。

  1. 如果注释掉“Sleep(50)”,大多数情况下(90%)我收到了错误的“数据”,具体来说,“totalLen”是正确的,但“数据”是错误的。
  2. 如果我将“Sleep(50)”替换为“Sleep(10)”,大约有一半的时间我收到了错误的“数据”,而“totalLen”也总是正确的。
  3. 如果我使用“Sleep(50)”,偶尔会收到错误的“数据”。

我可以保证,每次,我们的服务器都发送了正确的数据,并且客户端在 TCP 层也收到了正确的数据(我使用 WireShark 通过我使用的端口监视所有消息)。有没有人可以帮助回答为什么我的 C# 代码无法获取正确的数据?

另外,如果我使用mSocket.Available而不是Socket.Receive()的返回值来判断while循环,总能得到正确的数据和数据长度...

【问题讨论】:

  • 数据错误是什么意思?你得到很多零?还是垃圾?那个 ByteStreamWriter 是从哪里来的?那是一个 BinaryWriter 吗?
  • 你并不真正了解 TCP 是如何运作的。 TCP 将消息分成最大大小的数据报 ~1500 bytes. The Net library uses timers so datagrams can get futher split or combined. TCP also sends messages with zero bytes which is used as a keep-alive so the connection doesn't close. So when sending messages you must have a terminator for each message. You can use a combination of 3 techniques : 1) ASCII : Terminate with fixed char like '\' 2)Ascii 或 Binary:将长度添加到消息的开头 3)Ascii 或 Binary:固定长度的消息。接收需要读取消息直到找到结尾
  • @rene 对不起,我表达的不是很清楚。错误的数据就是垃圾,垃圾每次都是从第二个TCP数据报的第一个字节开始的。 ByteStreamWriter 是一个非常简单的 BinaryWriter,我可以保证它按预期工作。
  • @jdweng 抱歉,我认为我没有提供足够的信息。我每次都使用短连接,所以我假设只有当我们的服务器关闭连接时我才会收到 0,这也意味着一条消息已完成接收。
  • 无关:你的类不应该有终结器。您可能想阅读终结器最佳实践。

标签: c# .net sockets


【解决方案1】:

在写入之前不要将 temp 截断为 len 字节。 templen 之后的所有字节都没有被当前数据填充,因此包含无意义的陈旧日期。

复制到另一个流时,您可以使用stream.Write(temp, 0, len)

【讨论】:

  • 非常感谢!
猜你喜欢
  • 2014-05-09
  • 2014-02-21
  • 2019-12-27
  • 2020-09-19
  • 1970-01-01
  • 2022-08-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多