c# - .NET 应用程序最稳定的时间戳源是什么?

标签 c# datetime

基本问题

我的 C# 应用程序中的时间戳有问题。我从远程 TCP 连接异步接收数据。每次接收数据时,我都会将时间戳变量更新为 DateTime.Now .在一个单独的线程上,我每秒检查一次自上次接收以来它是否比预定义的超时时间长,如果是,则断开连接。这种方法已经工作了很多年,但现在我遇到了应用程序安装在时间源不稳定的机器上的情况。每隔几天,机器时间就会“自动更正”,我会过早断开连接。代码基本如下:

接收流程

void OnReceiveComplete(IAsyncResult ar) {
    ...
    mySocket.EndReceive(ar);
    lastRxTime = DateTime.Now;
    ...
}

检查流程
void CheckConnection() {
    TimeSpan ts = DateTime.Now.Subtract(lastRxTime);
    if(ts.TotalMilliseconds > timeout) {
        Disconnect(string.Format("No packet received from the server for over {0} seconds.", timeout / 1000));
    }
}

在问题期间和断开连接之前,我有有效的 Wireshark 捕获,我看到 NTP 流量最终看起来像是至少 1 分钟的更正。这显然会导致检查过程失败。

技术细节/对预期问题的回答
  • 我可以控制连接的两端,但不能控制中间的物理层(通常是低质量的卫星链路),这就是为什么要进行超时检查的原因。
  • 由于数据是异步的,如果在等于超时一半的时间段内没有从服务器发送数据,则可以发送一个小的心跳。
  • 此过程可以有多个实例(即在连接到多个不同服务器的机器上。
  • 所有通信都使用异步方法(据说使用完成端口)。
  • 检查过程在一个单独的线程上运行,该线程由一台机器上的所有客户端共享。

  • 已完成的研究(即可能的解决方案)

    到目前为止,我的 Google 搜索导致以下结果:
  • 我意识到我应该使用 DateTime.UtcNow而不是 DateTime.Now出于性能原因。这不会影响问题本身。
  • 依赖于 Ticks 的实现将是更好的解决方案。
  • 获取滴答声有两种选择 - Environment.TickCountStopwatch.GetTimestamp()
  • 根据我的研究,Environment.TickCount可能容易受到时间调整的影响,但我不确定在什么情况下。此外,由于我在其他更高性能的情况下使用相同的方法,因此 10-16 毫秒分辨率可能是一个问题(尽管不是在我在这里介绍的特定情况下)。
  • Stopwatch.GetTimestamp()可以回退到 DateTime.Now.Ticks当高性能时钟不可用时。我不确定这种情况发生的频率(是否有任何机器不再配备高性能时钟),但我确信如果它诉诸于 Ticks,同样的问题会发生。
  • 我也读过 Stopwatch.GetTimestamp()将使用 QueryPerformanceCounter() API 调用,从多个线程调用时可能会不稳定。

  • 终极问题

    我很好奇生成 lastRxTime 的最佳方法是什么?时间戳会是?我是否过于担心 Environment.TickCount 中的低可能性问题?和 Stopwatch.GetTimestamp()职能?我对替代实现持开放态度,只要它们考虑到应用程序的多线程特性以及链接质量问题。

    2013 年 7 月 17 日更新(已部署解决方案!)

    我已经部署了一个解决方案,想让每个人都了解细节。一般来说,可能没有一个公认的答案,但在经历了这次经历之后,我可以说最初的解决方案绝对是一个问题。我会尽量提供尽可能多的细节:

    首先,NTP 问题实际上是另一个问题的征兆。出现此问题的网络是 AD 域,其中两台运行我的代码的服务器设置为域 Controller 。事实证明,DC 是域的时间源。事实证明,在大约 11 天内,系统时间与这些系统上的实时时钟最多相差一分钟,此时 Windows 正在纠正错误。一旦它纠正了第一个 DC 上的滑动,第二个 DC 就会同步他的时间,并且都遇到了上述问题。

    根据反馈和我的原始研究,我创建了一个测试程序,在断开连接期间运行以记录 DateTime.Now、Environment.TickCount 和 Stopwatch.GetTimestamp() 的值。我发现在更正期间,Environment.TickCount 和 StopWatch.GetTimeStamp() 都没有下滑,这意味着它们是用作 DateTime.Now() 替代品的好选择。我选择了 TickCount,因为它保证在我部署的所有服务器上(而秒表可能会回退到我尚未找到的某些机器上的 DateTime 对象)。到目前为止它的工作没有问题。我对翻转问题进行了尽职调查,以防止该表格成为问题,但需要等待我的系统启动那么长时间才能确定。

    我想指出的是,如果其他人遇到类似的问题,他们不应该低估使用下面列表中任何其他提供的解决方案。每个都有自己的优点,事实上,简单的计数器可能是大多数情况下的最佳解决方案。我没有去这个解决方案的原因是我在不同的区域有类似的代码,这在很大程度上取决于时间紧迫。我在那里处理滴答计数的 16 毫秒左右的分辨率,但无法处理计数器解决方案引起的时间漂移​​(我在一个单独的产品中使用了类似的代码,该代码每小时漂移超过一秒并带来我超出项目规范)。

    再次感谢所有人,如果还有更多问题,我一定会更新问题。

    最佳答案

    Am I worrying too much about low likelihood issues in the Environment.TickCount and Stopwatch.GetTimestamp() functions?



    可能,是的。

    我更喜欢 Stopwatch.GetTimestamp() 选项。这将使用高性能计时器(如果它们在您的系统上可用),并回退到 DateTime.Ticks 如果那不是一个选择。因此,当在给定硬件上可用时,您将获得最佳计时,并且当高性能计时器不可用时,这是一个不错的选择作为后备。

    虽然 Environment.TickCount 可能是一个有趣的选择,如果您的机器可能运行超过 24.9 天,您将需要处理溢出的情况。

    关于c# - .NET 应用程序最稳定的时间戳源是什么?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17077249/

    相关文章:

    c# - JSON.NET反序列化自定义日期格式

    c# - Datetime.Now.Millisecond 不显示完整时间格式

    c# - 来自代码的带有样式和 DateTriggers 的 DataTemplate

    c# - StringBuilder 的 GetHashCode 生成不同的代码

    sql-server-2008 - SQL Server - 计算 HH :MM:SS format 中两个日期时间戳之间的耗时

    python - 将数据框中的日期拆分为 2 个单独的数据框

    java - 正则表达式查找字符串并根据条件替换它或添加额外字符

    c# - 对复制下数据库模式的持续开发的建议

    c# - 为什么 Tuple<T1...TRest> 中的 TRest 不受约束?

    MySQL InnoDB INTEGER 和 DATETIME 压缩