C# 对于.NET应用程序,最稳定的时间戳源是什么? 基本问题

C# 对于.NET应用程序,最稳定的时间戳源是什么? 基本问题,c#,datetime,C#,Datetime,我的C#申请中有一个时间戳问题。我从远程TCP连接异步接收数据。每次收到数据时,我都会将时间戳变量更新为DateTime.Now。在一个单独的线程上,我每秒检查一次,从上次接收以来,它是否已超过预定义的超时时间,如果是,则断开连接。这种方法已经工作了很多年,但是现在我遇到了这样一种情况,应用程序安装在一台时间源不稳定的机器上。每隔几天,机器时间就会“自动更正”,我会过早地断开连接。代码基本如下: 接收过程 检查过程 我在发布期间有有效的Wireshark捕获,就在断开连接之前,我看到NTP流量在

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

接收过程 检查过程 我在发布期间有有效的Wireshark捕获,就在断开连接之前,我看到NTP流量在至少1分钟的纠正中达到高潮。这显然会导致检查过程失败

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

  • 我意识到我应该使用
    DateTime.UtcNow
    而不是
    DateTime.Now,因为性能原因。这不会影响问题本身
  • 基于滴答声的实现是更好的解决方案
  • 获取滴答声有两个选项-
    Environment.TickCount
    Stopwatch.GetTimestamp()
  • 根据我的研究,
    Environment.TickCount
    可能会受到时间调整的影响,但我不确定在什么情况下。此外,由于我在其他更高性能的环境中使用了相同的方法,因此10-16毫秒的分辨率可能是一个问题(尽管不是我在这里介绍的特定情况下)
  • Stopwatch.GetTimestamp()
    可以回退到
    DateTime.Now.Ticks
    当高性能时钟不可用时。我不确定这种情况会发生多久(是否有任何机器不再配备高性能时钟),但我确信,如果它使用Ticks,同样的问题也会发生
  • 我还了解到,
    Stopwatch.GetTimestamp()
    将使用
    QueryPerformanceCounter()
    API调用,从多个线程调用时可能会不稳定
  • 终极问题 我很好奇生成
    lastRxTime
    时间戳的最佳方法是什么?我是否太担心
    环境中的低可能性问题。滴答声计数和
    Stopwatch.GetTimestamp()
    functions?我对其他实现持开放态度,只要它们考虑到应用程序的多线程性质以及链接质量问题

    更新7/17/2013(已部署解决方案!) 我已经部署了一个解决方案,希望让每个人都了解细节。一般来说,可能没有一个公认的答案,但在经历了这次经历后,我可以说,最初的解决方案肯定是一个问题。我将尽可能提供更多细节:

    首先,NTP问题实际上是另一个问题的症状。出现问题的网络是AD域,两台服务器运行我的代码,设置为域控制器。事实证明,DCs是域的时间源。此外,还证明,这些系统上的系统时间在应用程序上从实时时钟漂移了一分钟大约11天,此时Windows正在纠正滑差。一旦Windows纠正了第一个DC上的滑差,第二个DC将同步他的时间,两个DC都会遇到上述问题

    根据反馈和我最初的研究,我创建了一个测试程序,在断开连接期间运行,以记录DateTime.Now、Environment.TickCount和Stopwatch.GetTimestamp()的值。我发现在更正期间,Environment.TickCount和Stopwatch.GetTimestamp()都没有滑倒了,这意味着它们很适合用作DateTime.Now()的替代品。我选择了TickCount,因为它保证会出现在我所有部署的服务器上(而秒表可能会在一些我尚未找到的机器上退回到DateTime对象)。目前为止,它还没有出现问题。我在滚动问题上做了适当的努力,以防止出现问题,但需要等待我的系统运行那么长时间才能确定

    我想指出的是,如果其他任何人遇到类似的问题,他们不应忽视使用下面列表中的任何其他解决方案。每个解决方案都有自己的merrit,事实上,对于大多数情况,简单计数器可能是最佳解决方案。我之所以没有使用此解决方案,是因为我在separ中有类似的代码ate严重依赖于紧张时间的区域。我无法处理16毫秒左右的滴答计数分辨率,但无法处理计数器解决方案引起的时间漂移(我在一个单独的产品中使用了这样的代码,最终漂移超过一秒一小时,使我超出了项目的规格)

    再次感谢大家,如果有更多的问题出现,我一定会更新这个问题

    我是否太担心Environment.TickCount和Stopwatch.GetTimestamp()函数中的低可能性问题

    潜在的,
    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));
        }
    }
    
    private int m_conTimer;
    
    void OneSecondThreadCallback() {
        if (++m_conTimer >= TIMEOUT_VALUE)
            // Connection timed out. React accordingly.
    }
    
    // start the timer, callback in 10000 milliseconds, and don't fire more than once
    var timer = new Timer(TimerCallback, null, 10000, Timeout.Infinite);
    
    // to reset the timer when you receive data
    timer.Change(10000, Timeout.Infinite);
    
    // to stop the timer completely
    timer.Change(Timeout.Infinite, Timeout.Infinite);
    
    // and in your callback
    private void TimerCallback(object state)
    {
        // disconnect, or do whatever you want
    }
    
    // to set up the timer
    var timer = new Timer(10000) {AutoReset = false};
    timer.Elapsed += TimerOnElapsed;
    
    // to start the timer running
    timer.Start();
    
    // to reset the timer
    timer.Stop();
    timer.Start();
    
    // and the callback
    private void TimerOnElapsed(object sender, ElapsedEventArgs args)
    {
        // disconnect, or do whatever you want
    }