【问题标题】:How do I obtain the latency between server and client in C#?如何在 C# 中获取服务器和客户端之间的延迟?
【发布时间】:2010-10-20 17:39:56
【问题描述】:

我正在为使用 ActionScript 3 编写的游戏引擎开发 C# 服务器应用程序。我使用权威服务器模型来防止作弊并确保公平游戏。到目前为止,一切正常:

当客户端开始移动时,它告诉服务器并开始在本地渲染;然后,服务器告诉其他人客户端 X 已经开始移动,其中包含详细信息,以便他们也可以开始渲染。当客户端停止移动时,它会告诉服务器,服务器会根据客户端开始移动的时间和客户端渲染滴答延迟并回复所有人进行计算,以便他们可以使用正确的值进行更新。

问题是,当我在服务器计算中使用默认的 20 毫秒滴答延迟时,当客户端移动相当长的距离时,它停止时会明显向前倾斜。如果我将延迟稍微增加到 22 毫秒,在我的本地网络上,一切都运行得非常顺利,但在其他位置,倾斜仍然存在。在进行了一些实验之后,我注意到所需的额外延迟与客户端和服务器之间的延迟密切相关。我什至把它归结为一个很好用的公式:延迟 = 20 + (延迟 / 10)。

那么,我将如何继续获取某个客户端和服务器之间的延迟(我使用的是异步套接字)。 CPU 工作量不能太多,以免服务器运行缓慢。另外,这真的是最好的方法,还是有更有效/更简单的方法来做到这一点?

【问题讨论】:

    标签: c# .net sockets client-server latency


    【解决方案1】:

    很抱歉,这并不能直接回答您的问题,但一般来说,您不应过于依赖测量延迟,因为它可能会发生很大变化。不仅如此,您甚至不知道您测量的 ping 时间是否对称,这很重要。如果事实证明 20 毫秒的 ping 时间实际上是从服务器到客户端的 19 毫秒和从客户端到服务器的 1 毫秒,那么应用 10 毫秒的延迟校正是没有意义的。应用程序方面的延迟与网络方面的延迟不同 - 您可能能够 ping 某台机器并在 20 毫秒内获得响应,但如果您正在联系该机器上的服务器,该服务器每秒仅处理 50 次网络输入,那么您的响应将额外延迟 0 到 20 毫秒,这会发生不可预测的变化。

    这并不是说延迟测量在平滑预测方面没有作用,但它不会解决您的问题,只是稍微清理一下。

    从表面上看,这里的问题似乎是您在第一条消息中收到了信息,您使用该信息来推断数据,直到收到最后一条消息。如果其他一切都保持不变,那么第一条消息中给出的移动向量乘以消息之间的时间将为服务器提供客户端大致现在所处的正确结束位置(延迟/2)。但是,如果延迟发生变化,消息之间的时间将会增长或缩短。客户端可能知道他移动了 10 个单位,但服务器模拟他移动了 9 或 11 个单位,然后被告知将他恢复为 10 个单位。

    对此的一般解决方案是不要假设延迟会保持不变,而是发送定期位置更新,这允许服务器验证和更正客户端的位置。现在只有 2 条消息,在第 2 条消息之后发现并纠正了所有错误。消息越多,误差就会分布在更多的样本点上,从而可以进行更平滑且不那么明显的校正。

    但它永远不可能完美:只需要在移动的最后一毫秒出现延迟峰值,服务器的表示就会超调。如果您根据过去的事件来预测未来的运动,您将无法解决这个问题,因为没有真正的选择可以选择正确但迟到或不正确但及时,因为信息需要时间传播。 (责备爱因斯坦。)

    【讨论】:

      【解决方案2】:

      在使用基于ICMPpings 时要记住的一点是,网络设备通常会给ICMP 流量提供比普通 数据包更低的优先级,尤其是当数据包跨越网络边界(例如WAN)时链接。这可能会导致 ping 被丢弃或显示出比流量实际经历的延迟更高的延迟,并使其成为问题的指标,而不是衡量工具。

      在网络中越来越多地使用Quality of Service (QoS) 只会加剧这种情况,因此尽管 ping 仍然是一个有用的工具,但需要理解的是,它可能不是非网络延迟的真实反映。基于 ICMP 的真实流量。

      Itrinegy 博客How do you measure Latency (RTT) in a network these days? 上有一篇关于此的好帖子。

      【讨论】:

        【解决方案3】:

        您可以使用已经可用的Ping 类。应该优先于编写自己的恕我直言。

        【讨论】:

          【解决方案4】:

          有一个“ping”命令,您可以在其中将消息从服​​务器发送到客户端,然后计算获得响应所需的时间。除非出现 CPU 过载情况,否则它应该非常可靠。要获得单程旅行时间,只需将时间除以 2。

          【讨论】:

          • 根据应用的不同,几乎所有的消息都可以携带 ping 子包,而不会超出 1518 左右的字节限制。这样您就可以自动适应不断变化的网络天气。
          【解决方案5】:

          我们可以使用 .NET Framework 的Ping 类来测量round-trip time

          实例化Ping 并订阅PingCompleted 事件:

          Ping pingSender = new Ping();
          pingSender.PingCompleted += PingCompletedCallback;
          

          添加代码以配置和操作 ping。

          我们的 PingCompleted 事件处理程序 (PingCompletedEventHandler) 有一个 PingCompletedEventArgs 参数。 PingCompletedEventArgs.Reply 为我们提供了一个 PingReply 对象。 PingReply.RoundtripTime 返回往返时间(“发送 Internet 控制消息协议 (ICMP) 回显请求并接收相应 ICMP 回显回复消息所用的毫秒数”):

          public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
          {
              ...
              Console.WriteLine($"Roundtrip Time: {e.Reply.RoundtripTime}");
              ...
          }
          

          基于MSDN's example 的完整工作示例的代码转储。我已经修改它以将 RTT 写入控制台:

          public static void Main(string[] args)
          {
              string who = "www.google.com";
              AutoResetEvent waiter = new AutoResetEvent(false);
          
              Ping pingSender = new Ping();
          
              // When the PingCompleted event is raised,
              // the PingCompletedCallback method is called.
              pingSender.PingCompleted += PingCompletedCallback;
          
              // Create a buffer of 32 bytes of data to be transmitted.
              string data = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";
              byte[] buffer = Encoding.ASCII.GetBytes(data);
          
              // Wait 12 seconds for a reply.
              int timeout = 12000;
          
              // Set options for transmission:
              // The data can go through 64 gateways or routers
              // before it is destroyed, and the data packet
              // cannot be fragmented.
              PingOptions options = new PingOptions(64, true);
          
              Console.WriteLine("Time to live: {0}", options.Ttl);
              Console.WriteLine("Don't fragment: {0}", options.DontFragment);
          
              // Send the ping asynchronously.
              // Use the waiter as the user token.
              // When the callback completes, it can wake up this thread.
              pingSender.SendAsync(who, timeout, buffer, options, waiter);
          
              // Prevent this example application from ending.
              // A real application should do something useful
              // when possible.
              waiter.WaitOne();
              Console.WriteLine("Ping example completed.");
          }
          
          public static void PingCompletedCallback(object sender, PingCompletedEventArgs e)
          {
              // If the operation was canceled, display a message to the user.
              if (e.Cancelled)
              {
                  Console.WriteLine("Ping canceled.");
          
                  // Let the main thread resume. 
                  // UserToken is the AutoResetEvent object that the main thread 
                  // is waiting for.
                  ((AutoResetEvent)e.UserState).Set();
              }
          
              // If an error occurred, display the exception to the user.
              if (e.Error != null)
              {
                  Console.WriteLine("Ping failed:");
                  Console.WriteLine(e.Error.ToString());
          
                  // Let the main thread resume. 
                  ((AutoResetEvent)e.UserState).Set();
              }
          
              Console.WriteLine($"Roundtrip Time: {e.Reply.RoundtripTime}");
          
              // Let the main thread resume.
              ((AutoResetEvent)e.UserState).Set();
          }
          

          您可能希望执行几次 ping,然后计算平均值,这当然取决于您的要求。

          【讨论】:

          • 我可以跨过具有这种 Ping 机制的系统。发生的事情是,在高流量偷看时,Ping 消息在同一缓冲区中的其他有效负载后面,并且当它返回时它是“迟到的”。您将如何解决这种情况?
          • 好问题,但对于微软而不是我! Ping 类是 .NET 的一部分,该示例基于 MSDN 中的示例。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-03-17
          • 1970-01-01
          • 1970-01-01
          • 2016-11-12
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多