JavaでNTPサーバーから正しい時刻を取得する方法

Java

ネットワーク・タイム・プロトコル(NTP)を使用してNTPサーバーに問い合わせることで正しい日付と時刻を取得するサンプルを実装してみました。

サンプル実装

package sample.ntp;

import java.net.InetAddress;
import java.text.SimpleDateFormat;
import java.util.Date;

import org.apache.commons.net.ntp.NTPUDPClient;
import org.apache.commons.net.ntp.NtpV3Packet;
import org.apache.commons.net.ntp.TimeInfo;

public class NtpSample {

    /**
     * NTPサーバー
     * <p>
     * 独立行政法人情報通信研究機構 (NICT)<br>
     * http://jjy.nict.go.jp/tsp/PubNtp/index.html
     * </p>
     */
    private static final String NTP_SERVER = "ntp.nict.jp";

    public static void main(String[] args) {
        SimpleDateFormat formater = new SimpleDateFormat("HH:mm:ss.SSS");
        NTPUDPClient client = new NTPUDPClient();
        try {
            client.open();
            InetAddress host = InetAddress.getByName(NTP_SERVER);
            TimeInfo info = client.getTime(host);

            info.computeDetails();
            Date exactTime = new Date(System.currentTimeMillis() + info.getOffset());
            System.out.println("正しい時刻\n" + formater.format(exactTime) + "\n");

            NtpV3Packet packet = info.getMessage();

            System.out.println("[t1] クライアントがパケットを送信した時刻(Originate TimeStamp)\n"
                    + formater.format(packet.getOriginateTimeStamp().getDate()) + "\n");

            System.out.println("[t2] NTPサーバーがパケットを受信した時刻(Receive TimeStamp)\n"
                    + formater.format(packet.getReceiveTimeStamp().getDate()) + "\n");

            System.out.println("[t3] NTPサーバーがパケットを送信した時刻(Transmit TimeStamp)\n"
                    + formater.format(packet.getTransmitTimeStamp().getDate()) + "\n");

            System.out.println("[t4] クライアントがパケットを受信した時刻(Return TimeStamp)\n"
                    + formater.format(new Date(info.getReturnTime())) + "\n");

            System.out.println("往復にかかった時間(Roundtrip Time)\n" + info.getDelay() + "ms\n");
            System.out.println("クライアントの時刻差(Offset)\n" + info.getOffset() + "ms\n");

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            client.close();
        }
    }
}

Apache Commons Net ライブラリを使用しました。

  • NTPUDPClient ・・・ NTPサーバーへ問い合わせするクライアントクラス
  • TimeInfo ・・・ NTPサーバーの応答パケットを保管するクラス
  • NtpV3Packet ・・・ NTPサーバーの応答パケットのインターフェース

処理の流れ

  • NTPUDPClientのオブジェクトを生成
  • NTPサーバーのホスト名を渡してInetAddressオブジェクトを生成
  • NTPUDPClientのgetTimeメソッドにInetAddressオブジェクトを渡してNTPサーバーの時刻情報を取得
  • TimeInfoのcomputeDetailsメソッドで詳細情報を計算
  • 時刻情報を元に正しい時刻と補足情報等を出力して終了

実行結果

クライアント側OSの時刻を2分程遅らせて実行してみました。
t1~t4の時刻については後述するNTPの動作原理に合わせて出力しています。

正しい時刻
17:02:05.121

[t1] クライアントがパケットを送信した時刻(Originate TimeStamp)
17:00:00.962

[t2] NTPサーバーがパケットを受信した時刻(Receive TimeStamp)
17:02:05.082

[t3] NTPサーバーがパケットを送信した時刻(Transmit TimeStamp)
17:02:05.082

[t4] クライアントがパケットを受信した時刻(Return TimeStamp)
17:00:01.036

往復にかかった時間(Roundtrip Time)
74ms

クライアントの時刻差(Offset)
124083ms

pom.xml

Mavenであれば pom.xml に下記のように依存関係を追加するとApache Commons Netが利用できるようになります。

<dependency>
  <groupId>commons-net</groupId>
  <artifactId>commons-net</artifactId>
  <version>3.6</version>
</dependency>

ネットワーク・タイム・プロトコル (NTP)について

NTPについて@ITの下記の記事が参考になりました。

第1回 NTPとWindows時刻同期サービス (1/2):Windowsネットワーク時刻同期の基礎とノウハウ(改訂版) – @IT

Screenshot of www.atmarkit.co.jp

特にNTPの動作原理「t1~t4」の部分が理解できると処理の流れがつかめると思います。

t1 =[クライアントがクエリを発信した時刻]

t2 =[サーバがクエリを受信した時刻]

t3 =[サーバが応答を発信した時刻]

t4 =[クライアントが応答を受信した時刻]

NTPサーバーについて

独立行政法人情報通信研究機構 (NICT)のNTPサーバー(ntp.nict.jp)を使用しました。
無料で個人利用可能ですが負荷をかけてしまわないように注意する必要があります。

FAQに丁寧な説明があったので抜粋します。

  • [Q.0-1] 申請は基本的に必要ない。
    ただし、1時間平均で20回(3分に1回程度)、 1日の合計が480回 を超える場合は事前連絡が必要。
  • [Q.0-2] 無料で利用可能。
  • [Q.1-1] Windows、Macでの設定方法の説明あり。
  • [Q.1-2] IPアドレスではなくホスト名(ntp.nict.jp)を設定してほしい。
  • [Q.1-7] 個人利用可能。
    インターネットサービスプロバイダや企業、大学等でNTPサーバーがあるならそっちを利用したほうがいいかも。
  • [Q.2-1] stratum 1 で基準としている時計は「日本標準時」
  • [Q.2-2] サーバのタイムスタンプ精度は日本標準時に対して 10ナノ秒以内
  • [Q.2-6] 対応時刻同期プロトコルは RFC1305 および RFC4330 に準拠

その他、製品に初めから設定されているWindowsのNTPサーバー(time.windows.com)や、AppleのNTPサーバー(time.apple.com)等がありますが、こちらは常に負荷が高いためあまり使用しないほうがいいようです。

タイトルとURLをコピーしました