端末時刻のズレが酷かったのでNTPサーバーから時刻を取得するようにした。


・とりあえず動けばいい人用

 先にソースコードを置いておく。外部ライブラリ(org.apache.commons.net.ntp)を使う。
 最初はそのへんで拾ったソースコードをコピペすれば動くだろうと思っていたが、どうもうまくいかなかったのでJavadocを頼りにもがもがした結果、コピペ元のソースに6行目のところが不足していたことが判明した。
 なお、取得しているのはサーバーとクライアントの時計のズレ(offset)である。
 serverNameにはNTPサーバーのアドレスを入れる。0.jp.pool.ntp.orgとかntp.nict.jpとかがいいだろう。

NTPUDPClient client = new NTPUDPClient();
try {
  InetAddress address = InetAddress.getByName(serverName);
  client.open();
  TimeInfo ti = client.getTime(address);
  ti.computeDetails();
  long offset = ti.getOffset();
  System.out.println("Offset:" + offset);
} catch (Exception e) {
  e.printStackTrace();
}

・そもそもNTPとは

 NTPは Network Time Protocolの略。ネットワークを使って、コンピュータ同士の時刻を同期させるプロトコルのこと。
 簡単な原理を説明すると、

 1. クライアントが時刻 \(t_1\) にリクエストを送信する。
 2. サーバーは時刻 \(t_2\) にリクエストを受信する。
 3. サーバーが時刻 \(t_3\) に応答する。
 4. クライアントは時刻 \(t_4\) に応答を受け取る。

 ただしここで \(t_1\) ~ \(t_4 \)は各々の機器の時刻である。図にするとこうだ。

 ここから、サーバーとクライアントの時刻の差を求めるには、まずリクエストの往復にかかった時間(Roundtrip Time)を求める。これは簡単で、この時間を\(d\)とおくと
$$d=(t_4-t_1)-(t_3-t_2)\;\;\;\;\cdots(1)$$となるのはおわかりだろう。
 ここで、クライアントの時刻が \(\Delta t\) だけ遅れているとすると、クライアントの時刻 \(t_1\) はサーバーでは \(t_1+\Delta t\) になる。また、往路と復路でかかる時間は等しいと仮定すると、送信したリクエストが到達するのに\(\displaystyle \frac d2\) だけ経過するから、サーバーがリクエストを受け取る時刻 \(t_2\) は
$$t_2=t_1+\frac d2 +\Delta t$$となる。この式に(1)を代入すると、$$\Delta t=\frac{(t_2-t_1)+(t_3-t_4)}{2}$$となる。これがサーバーとクライアントの時刻のズレになる。ただ、これは往路と復路の所要時間が等しいと仮定した結果だから、誤差は最大で\(\displaystyle\pm\frac d2\)だけ発生しうる。
以上を踏まえてより原理的なソースコードを書くと以下のようになる。

NTPUDPClient client = new NTPUDPClient();
try {
  InetAddress address = InetAddress.getByName(serverName);
  client.open();
  TimeInfo ti = client.getTime(address);
  NtpV3Packet packet = ti.getMessage();
  long t1 = packet.getOriginateTimeStamp().getTime();
  long t2 = packet.getReceiveTimeStamp().getTime();
  long t3 = packet.getTransmitTimeStamp().getTime();
  long t4 = ti.getReturnTime();
  long offset = (t2 - t1 + t3 - t4)/2;
  System.out.println("Offset:" + offset);
} catch (Exception e) {
  e.printStackTrace();
}


 以上。外部ライブラリってべんりですね

【Java】NTPサーバーから時刻を取得する

投稿ナビゲーション


コメントを残す

メールアドレスが公開されることはありません。