ベース発行
C# アプリケーションのタイム スタンプに問題があります。リモート TCP 接続から非同期でデータを受信しています。データを受け取るたびに、タイムスタンプ変数を に更新しDateTime.Now
ます。別のスレッドで、1 秒に 1 回、最後の受信から事前定義されたタイムアウト期間よりも長いかどうかを確認し、そうであれば切断します。この方法は長年機能してきましたが、現在、タイム ソースが不安定なマシンにアプリケーションがインストールされている状況があります。数日ごとに、マシンの時刻が「自動修正」され、途中で接続を切断します。コードは基本的に次のとおりです。
受信プロセス
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 キャプチャがあり、切断の直前に、少なくとも 1 分の修正のように見える NTP トラフィックが最高潮に達していることがわかります。これにより、明らかにチェック プロセスが失敗します。
技術的な詳細/予想される質問への回答
- 接続の両端を制御できますが、その間の物理層 (多くの場合、低品質の衛星リンク) は制御できません。これが、このタイムアウト チェックが行われている理由です。
- データは非同期であるため、タイムアウトの半分に等しい時間サーバーからデータが送信されない場合、小さなハートビートを送信するための規定があります。
- このプロセスの複数のインスタンスが存在する可能性があります (つまり、複数の異なるサーバーに接続しているマシン上で.
- すべての通信は非同期メソッドを使用します (これはおそらく完了ポートを使用します)。
- チェック プロセスは、単一のマシン上のすべてのクライアントによって共有される別のスレッドで実行されます。
完了した調査 (つまり、可能な解決策)
この時点までの私のGoogle検索は、次のことにつながりました。
- パフォーマンス上の理由から、
DateTime.UtcNow
代わりに使用する必要があったことに気づきました。DateTime.Now
これは問題自体には影響しません。 - ティックに依存する実装は、より良い解決策です。
- ティックを取得するには 2 つのオプションがあります
Environment.TickCount
。Stopwatch.GetTimestamp()
- 私の調査によると、
Environment.TickCount
時間調整の影響を受けやすい可能性がありますが、どのような状況であるかはわかりません。また、私はこれと同じ方法論を他のより高いパフォーマンスの状況で使用しているため、10 ~ 16 ミリ秒の解像度が問題になる可能性があります (ただし、ここで提示している特定のケースではありません)。 - は、高性能クロックが利用できない場合に
Stopwatch.GetTimestamp()
フォールバックできます。DateTime.Now.Ticks
それがどのくらいの頻度で発生するかはわかりませんが(高性能クロックが搭載されていないマシンはありますか)、Ticksに頼ると同じ問題が発生することは確かです. Stopwatch.GetTimestamp()
API呼び出しを使用することも読みましたがQueryPerformanceCounter()
、複数のスレッドから呼び出されると不安定になる可能性があります。
究極の質問
lastRxTime
タイムスタンプを生成する最良の方法は何ですか? Environment.TickCount
およびStopwatch.GetTimestamp()
関数の可能性が低い問題について心配しすぎていませんか? アプリケーションのマルチスレッドの性質とリンクの品質の問題を考慮している限り、代替の実装を受け入れます。
UPDATE 7/17/2013 (解決策が展開されました!)
私はソリューションを展開しましたが、詳細については全員に説明したいと考えています。一般的に、受け入れられた答えは 1 つではありませんが、この経験を経て、元の解決策が間違いなく問題であったと言えます。できる限り詳細を提供するように努めます。
まず、NTP の問題は、実際には別の問題の兆候でした。問題が発生しているネットワークは、私のコードを実行している 2 台のサーバーがドメイン コントローラーとして設定されている AD ドメインです。DC はドメインのタイム ソースであることがわかります。また、システム時刻がこれらのシステムのリアルタイム クロックから約 11 日間にわたって最大 1 分ずれていることも判明しており、その時点で Windows はずれを修正しています。最初の DC のずれを修正すると、2 番目の DC が時刻を同期し、両方が上記の問題に遭遇します。
フィードバックと独自の調査に基づいて、切断中に実行して DateTime.Now、Environment.TickCount、および Stopwatch.GetTimestamp() の値を記録するテスト プログラムを作成しました。私が見つけたのは、修正中に Environment.TickCount も StopWatch.GetTimeStamp() もまったくずれていなかったことです。つまり、DateTime.Now() の代わりとして使用するのに適した候補でした。TickCount を使用したのは、デプロイされたすべてのサーバーに存在することが保証されているためです (ストップウォッチは、まだ見つけていない一部のマシンでは DateTime オブジェクトにフォールバックする可能性があります)。これまでのところ問題なく動作しています。フォームが問題になるのを防ぐために、ロールオーバーの問題について十分な注意を払いましたが、システムが稼働するまで待つ必要があります。
他の誰かが同様の問題を経験している場合、以下のリストに示されている他の解決策の使用を軽視してはならないことに注意してください. それぞれに独自のメリットがあります。実際、ほとんどの状況では、単純なカウンターが最適なソリューションである可能性があります。このソリューションに行かなかった理由は、タイトなタイミングに大きく依存する別の領域に同様のコードがあるためです。私はそこでティックカウントの16ミリ秒程度の解像度を処理できますが、カウンターソリューションが発生する時間のずれを処理できません(別の製品でそのようなコードを使用し、1時間に1秒以上ずれてしまい、私を連れてきましたプロジェクトの仕様外)。
繰り返しになりますが、すべてに感謝します。さらに質問があれば、必ず質問を更新します。