8

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

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

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

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

4

5 回答 5

13

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

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

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

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

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

于 2009-04-25T09:52:21.097 回答
5

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

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

Itrinegy 博客上有一篇很好的文章,这些天您如何测量网络中的延迟 (RTT)?对这个。

于 2009-11-04T03:09:22.493 回答
4

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

于 2009-04-25T10:56:57.557 回答
2

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

于 2009-04-21T04:02:22.700 回答
1

我们可以使用.NET Framework 的类来测量往返时间。Ping

实例化 aPing并订阅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 示例的完整工作示例的代码转储。我已经修改它以将 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 操作,然后计算平均值,具体取决于您的要求。

于 2018-03-12T13:26:37.783 回答