【问题标题】:C# socket abnormal latencyC# 套接字异常延迟
【发布时间】:2010-09-30 04:25:34
【问题描述】:

我正在使用 C# 和 XNA 开发一款小型在线多人乒乓球游戏。

我使用套接字在我个人 LAN 上的两台计算机之间传输数据。它工作正常。

问题在于速度:传输速度很慢。

当我 ping 第二台计算机时,它显示 2 毫秒的延迟。 我在我的代码中设置了一个小计时器,它显示大约 200 毫秒的延迟。 即使服务器和客户端在同一台计算机上(使用 127.0.0.1),延迟仍然在 15 毫秒左右。我认为这很慢。

这是我的一些代码片段:

server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
server.Bind(new IPEndPoint(IPAddress.Any, port));
server.Listen(1);
// Begin Accept
server.BeginAccept(new AsyncCallback(ClientAccepted), null);

在 ClientAccepted 中,我设置了一个 NetworkStream、一个 StreamReader 和一个 StreamWriter。 当我想更新玩家的位置时,这就是我发送消息的方式:

string message = "P" + "\n" + player.Position + "\n";
byte[] data = Encoding.ASCII.GetBytes(message);
ns.BeginWrite(data, 0, data.Length, new AsyncCallback(EndUpdate), null);

EndUpdate 唯一要做的就是调用 EndWrite。 这就是我接收数据的方式:

message = sr.ReadLine();

它不会阻止我的游戏,因为它在第二个线程上。

这些是我尝试过的东西: - 使用 IP 而不是 TCP - 使用二进制消息而不是文本 - 使用 IPv6 而不是 IPv4 没有任何帮助。

关于如何改善延迟有什么想法吗?

谢谢

【问题讨论】:

    标签: c# sockets latency


    【解决方案1】:

    您看到的延迟很可能是由于Nagle's algorithm 造成的,该Nagle's algorithm 用于提高使用TCP 发送大量小数据包时的效率。本质上,TCP 堆栈将小数据包收集在一起,并在传输之前将它们合并成一个更大的数据包。这显然意味着延迟一小段时间 - 最多 200 毫秒 - 以查看是否有更多数据发送,然后再发送输出缓冲区中等待的数据。

    所以尝试通过在套接字上将NoDelay 属性设置为 true 来关闭 Nagle。

    但是,请注意,如果您对套接字执行大量小型写入,则可能会因为 TCP 标头开销和每个数据包的处理而降低性能。在这种情况下,如果可能的话,你会想尝试将你的写入分批收集。

    【讨论】:

      【解决方案2】:

      还有其他几个元问题需要考虑:

      1. 计时器精度:许多系统计时器仅每 15 毫秒左右更新一次。我不记得确切的值,但是如果您的时间总是大约 15 毫秒的倍数,请怀疑您的计时器精度不高。这“可能”不是您的问题,但请记住。

      2. 如果您在同一台计算机上进行测试,并且只运行单核机器,则线程/进程切换频率将支配您的 ping 时间。没什么可做的,只是尝试添加 Sleep(0) 调用以在发送数据后允许线程/进程交换。

      3. TCP/IP 传输设置。正如前面在 SocketOptionName.NoDelay 中提到的。同样如前所述,如果您不断更新状态,请考虑使用 UDP。不能保证顺序,但是对于易失性数据,丢失数据包是可以接受的,因为无论如何状态都会很快被覆盖。为避免使用过时的数据包,请向数据包添加一个递增值,并且永远不要使用标记早于当前状态的数据包。 tcp 和 udp 之间的差异不应占 200 ms,但其他因素的组合可以。

      4. 轮询与选择。如果您正在轮询接收到的数据,那么轮询频率自然会干扰接收速率。我在专门的网络线程中使用 Socket.Select 来等待传入的数据并尽快处理。

      【讨论】:

        【解决方案3】:

        大多数网络游戏不使用 TCP 而是使用 UDP 进行通信。这样做的问题是数据很容易被丢弃,这是您必须考虑的问题。

        使用 TCP,主机/服务器之间有许多交互来保证数据以有序的方式(使用 UDP 时您必须考虑的另一件事,即数据没有有序的事实)。

        最重要的是,您代码中的计时器必须等到字节从非托管套接字层通过非托管代码等冒泡为止。那里会有一些您不会的开销能够克服(非托管到托管的过渡)。

        【讨论】:

        • 另外,ping 使用 UDP 而不是 TCP。
        • UDP 确实以可靠性换取效率,但 TCP 协议开销并不能解释 15 毫秒的延迟,更不用说 200 毫秒的延迟了。
        【解决方案4】:

        您说您尝试了“IP”,并且您可能通过指定 ProtocolType.Ip 来做到这一点,但我不知道这真正意味着什么(即我不知道在“幕后”选择了什么协议)你将它与 SocketType.Stream 结合起来。

        正如 casperOne 所说,如果您使用 TCP,请查看设置 SocketOptionName.NoDelay 是否对您有任何影响。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-06-05
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-23
          • 2013-03-13
          相关资源
          最近更新 更多