ラベル UDP の投稿を表示しています。 すべての投稿を表示
ラベル UDP の投稿を表示しています。 すべての投稿を表示

2015年5月18日月曜日

C#でNTPを使ってみる

C#のUDPでNTPにアクセスしてみました
今回使ったサーバーは ntp.nict.jp です(つまりStratum2以上のサーバーでの動作確認はしていません)

実行するとこんな感じになります

nictサーバーでStratumは1 クロックソース識別子は"NICT"です

5個の時間は上から
・サーバーがクロックソースと最後に同期した時間(サーバー時間)
・クライアントがリクエストを送信した時間(クライアント時間)
・サーバーがリクエストを受信した時間(サーバー時間)
・サーバーがレスポンスを送信した時間(サーバー時間)
・クライアントがレスポンスを受信した時間(クライアント時間)
となります
NTPでは2種類の時間系が存在することに注意してください

最初の1つを除いた4個の時間がそれぞれ ts, Tr, Ts, tr となり、サーバーから見たクライアントの遅延は ((Ts + Tr) / 2) + ((ts + tr) / 2) となります
クライアントの時間が進んでいる場合は負の値になります

NTP時間は整数32bit 小数点以下32bitの固定小数点形式です
C#で時間を計算する場合は、Math.Pow(2, -32)をNTPtime(UInt64)に掛けた積が秒となり、1900年1月1日0時0分0秒UTC±0からの経過時間です

C#ではシステム時間を変更することができるので、クライアント時間の遅延量がわかれば適切に修正できますが、気軽にシステム時間を変更するべきではありません
一気に時間を飛ばすと不都合が発生する可能性があるからです(もっとも、Windowsの標準機能でも一気に時間を飛ばしていますが。。。)

***

NTPと通信する場合は、リクエストを送信する直前に送信時間を記録し、レスポンスを受信した直後に受信時間を記録します
送信時間と受信時間の処理はNTPの精度に大きく影響しますが、その前後は精度にはさほど影響しません(もちろんレスポンスを受信してから何日もほっといてしまえば精度は劣化しますが、常識的な処理速度であれば問題ないでしょう)

今回のプログラムは送信前に時間を記録しておき、レスポンスが一定時間なければタイムアウトの例外を投げます
タイムアウトは初期値で2秒で、その間はブロッキングです
一応200ミリ秒程度でレスポンスが帰ってきますが、回線状態によってはもう少し必要な場合もあります

NTPのエンディアンはビッグエンディアンですから、C#のBitConverterのような簡単な処理方法は使えません そのためいちいちビットシフト等で計算しています

***

なぜシステム時間を変更するわけでもないのにNTPの時間を使うかというと、クライアントの遅延時間がわかれば、C#のAddSecondsなどで簡単に補正することが可能だからです

例えば人工衛星の位置を計算したい場合、相手は秒速8km前後で移動しているわけで、システム時間が30秒もズレていたら相手はおよそ250kmも移動してしまいます ISSなどは高度400km程度ですから、250kmというのは無視できません(35度くらい移動してしまいます)

ただNTPは常に使えるわけではなく、最低限何らかのインターネット接続できる環境が必要なので、できればPCの時間は常に構成しておくほうが望ましいです(Windowsって定期的にNTPと同期するはずなのにかなりズレてますよね。。。)

WindowsでNTPと同期した直後に以下のプログラムを走らせても数十ミリ秒位のズレがあるので、あんまり高精度は期待しない方がいいです

それと、最近のNTPでは「64bitNTPtimeの最上位bitが0の場合は2036年以降として処理する」みたいな約束がありますが、以下のプログラムでは未対応なので、あと20年くらいで使えなくなります
ま、20年後にこのコードを使う人もいないと思いますが、注意してください