我的C#应用程序中存在时间戳问题.我异步地从远程TCP连接接收数据.每次收到数据时,我都会将时间戳变量更新为DateTime.Now.在一个单独的线程上,每秒一次,我检查它是否超过自我上次收到后的预定义超时时间,如果是,则断开连接.这种方法已经工作了很多年,但现在我的情况是应用程序安装在具有不稳定时间源的机器上.每隔几天,机器时间"自动校正",我提前断开连接.代码基本如下:
void OnReceiveComplete(IAsyncResult ar) {
...
mySocket.EndReceive(ar);
lastRxTime = DateTime.Now;
...
}
Run Code Online (Sandbox Code Playgroud)
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));
}
}
Run Code Online (Sandbox Code Playgroud)
在问题发生期间我有有效的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时间戳的最佳方法是什么?我是否过分担心Environment.TickCount和Stopwatch.GetTimestamp()功能中的低可能性问题?只要他们将应用程序的多线程特性以及链路质量问题考虑在内,我就会对其他实现持开放态度.
我已经部署了一个解决方案,并希望让每个人都了解详细信息.一般来说,可能没有一个可接受的答案,但在经历了这个经历之后,我可以说最初的解决方案肯定是一个问题.我会尽量提供尽可能详细的信息:
首先,NTP问题实际上是一个不同问题的症状.出现此问题的网络是AD Domain,其中两台服务器运行我的代码设置为域控制器.事实证明,DC是域的时间源.事实证明,系统时间从这些系统上的实时时钟漂移了大约11天的一分钟,此时Windows正在纠正滑动.一旦它校正了第一个DC上的滑差,第二个DC就会同步他的时间并且都会遇到上述问题.
根据反馈和我原来的研究,我创建了一个测试程序,用于在断开日期时间,Date.TickCount和Stopwatch.GetTimestamp()的日志值时运行.我发现在校正过程中,Environment.TickCount和StopWatch.GetTimeStamp()都没有滑落,这意味着它们很适合用作DateTime.Now()的替代品.我选择了TickCount,因为它保证在我所有已部署的服务器上(而秒表可以回退到我尚未找到的某些机器上的DateTime对象).它到目前为止没有问题.我在滚动问题上做了尽职尽责,以防止这种形式出现问题,但需要等待我的系统长时间确定.
我想要指出的是,如果其他人遇到类似的问题,他们不应该忽视下面列表中任何其他呈现的解决方案的使用.每个都有自己的优点,事实上简单的计数器可能是大多数情况下的最佳解决方案.我没有采用这种解决方案的原因是我在单独的区域中有类似的代码,严重依赖于紧密的时间.我可以处理那里的滴答计数的16毫秒左右的分辨率,但无法处理计数器解决方案产生的时间漂移(我在一个单独的产品中使用了这样的代码,这个产品每小时漂移超过一秒钟并带给我超出项目规范).
再次,感谢所有人,如果还有更多,我一定会更新问题.
我是否过于担心 Environment.TickCount 和 Stopwatch.GetTimestamp() 函数中的低可能性问题?
有可能,是的。
我更喜欢这个Stopwatch.GetTimestamp()选择。如果您的系统上可用,这将使用高性能计时器;DateTime.Ticks如果这不是一个选项,则回退到该计时器。因此,在给定硬件上可用时,您将获得尽可能最佳的计时,并且在高性能计时器不可用时作为后备方案是一个不错的选择。
虽然Environment.TickCount这可能是一个有趣的选择,但如果您的计算机可能运行超过 24.9 天,您将需要处理溢出的情况。
| 归档时间: |
|
| 查看次数: |
1224 次 |
| 最近记录: |