All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.time4j.clock.SntpConnector Maven / Gradle / Ivy

/*
 * -----------------------------------------------------------------------
 * Copyright © 2013-2015 Meno Hochschild, 
 * -----------------------------------------------------------------------
 * This file (SntpConnector.java) is part of project Time4J.
 *
 * Time4J is free software: You can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published
 * by the Free Software Foundation, either version 2.1 of the License, or
 * (at your option) any later version.
 *
 * Time4J is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Time4J. If not, see .
 * -----------------------------------------------------------------------
 */

package net.time4j.clock;

import net.time4j.Moment;
import net.time4j.SystemClock;
import net.time4j.scale.TimeScale;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.util.ServiceLoader;


/**
 * 

Connects to a modern time server using the NTP-protocol.

* *

This class needs a socket connection via the port 123. The exact * configuration can be set in the constructors but can also be changed * at runtime. It is recommended not to connect to the NTP-server during * or near a leap second because the NTP-protocol only repeats such a * timestamp causing ambivalences.

* * @author Meno Hochschild * @since 2.1 * @doctags.concurrency {threadsafe} */ /*[deutsch] *

Nimmt die Verbindung zu einem modernen Uhrzeit-Server gemäß * dem NTP-Protokoll auf.

* *

Diese Klasse benötigt einen Internet-Zugang über * Port 123 (NTP). Die genaue Konfiguration wird zunächst im * Konstruktor festgelegt, kann aber zur Laufzeit geändert werden. * Es wird empfohlen, nicht während oder nahe einer Schaltsekunde * zum NTP-Server zu verbinden, weil das NTP-Protokoll solch einen * Zeitstempel nur wiederholt und sich somit hier ambivalent zeigt.

* *

Die Physikalisch-Technische Bundesanstalt in Braunschweig (PTB), * die dort eine Atomuhr betreibt, benötigt als Adresse den Wert * "ptbtime1.ptb.de" und das Protokoll NTP4. Eine Alternative * ist die Adresse "ptbtime2.ptb.de".

* * @author Meno Hochschild * @since 2.1 * @doctags.concurrency {threadsafe} */ public class SntpConnector extends NetTimeConnector { //~ Statische Felder/Initialisierungen -------------------------------- private static final int MIO = 1000000; //~ Instanzvariablen -------------------------------------------------- private volatile SntpMessage lastReply = null; //~ Konstruktoren ----------------------------------------------------- /** *

Creates a new instance which is configured by a * {@code ServiceLoader}.

* * @throws IllegalStateException if no configuration could be found * @see ServiceLoader */ /*[deutsch] *

Konstruiert eine neue Instanz, die über einen * {@code ServiceLoader} konfiguriert ist.

* * @throws IllegalStateException if no configuration could be found * @see ServiceLoader */ public SntpConnector() { super(initConfiguration()); } /** *

Creates a new instance which is configured by given argument.

* * @param ntc SNTP-configuration */ /*[deutsch] *

Konstruiert eine neue Instanz, die wie angegeben * konfiguriert ist.

* * @param ntc vorgesehene Konfiguration */ public SntpConnector(SntpConfiguration ntc) { super(ntc); } /** *

Creates a new instance which uses a default configuration * using the specified NTP4-server.

* *

Example:

* *
     *  SntpConnector clock = new SntpConnector("ptbtime1.ptb.de");
     *  clock.connect();
     *  System.out.println(clock.currentTime());
     * 
* * @param server NTP4-server */ /*[deutsch] *

Konstruiert eine neue Instanz, die zum angegebenen NTP-Server * verbindet.

* *

Beispiel:

* *
     *  SntpConnector clock = new SntpConnector("ptbtime1.ptb.de");
     *  clock.connect();
     *  System.out.println(clock.currentTime());
     * 
* * @param server NTP4-server */ public SntpConnector(String server) { super(new SimpleNtpConfiguration(server)); } //~ Methoden ---------------------------------------------------------- /** *

Returns the current time in milliseconds since the Unix epoch * [1970-01-01T00:00:00,000Z].

* *

UTC leap seconds are never counted.

* * @return count of milliseconds since UNIX-epoch without leap seconds * @since 2.1 */ /*[deutsch] *

Liefert die aktuelle Zeit in Millisekunden seit dem Beginn der * UNIX-Epoche, nämlich [1970-01-01T00:00:00,000Z].

* *

Es handelt sich immer um eine Zeitangabe ohne UTC-Schaltsekunden.

* * @return count of milliseconds since UNIX-epoch without leap seconds * @since 2.1 */ public long currentTimeInMillis() { if (!this.isRunning()) { Moment m = this.currentTime(); return ( m.getPosixTime() * 1000 + m.getNanosecond() / MIO ); } long millis = SystemClock.MONOTONIC.currentTimeInMillis(); return (millis + (this.getLastOffset(millis * 1000) / 1000)); } /** *

Returns the current time in microseconds since the Unix epoch * [1970-01-01T00:00:00,000000Z].

* *

UTC leap seconds are never counted.

* * @return count of microseconds since UNIX-epoch without leap seconds * @since 2.1 */ /*[deutsch] *

Liefert die aktuelle Zeit in Mikrosekunden seit dem Beginn der * UNIX-Epoche, nämlich [1970-01-01T00:00:00,000000Z].

* *

Es handelt sich immer um eine Zeitangabe ohne UTC-Schaltsekunden.

* * @return count of microseconds since UNIX-epoch without leap seconds * @since 2.1 */ public long currentTimeInMicros() { if (!this.isRunning()) { Moment m = this.currentTime(); return ( m.getPosixTime() * MIO + m.getNanosecond() / 1000 ); } long micros = SystemClock.MONOTONIC.currentTimeInMicros(); return (micros + this.getLastOffset(micros)); } /** *

Returns the last received message of the NTP-server.

* * @return server message or {@code null} if not yet received * @since 2.1 * @see #connect() */ /*[deutsch] *

Liefert die zuletzt erhaltene Nachricht des Servers.

* * @return Server-Nachricht oder {@code null}, wenn noch nicht empfangen * @since 2.1 * @see #connect() */ public SntpMessage getLastReply() { return this.lastReply; } @Override protected Moment doConnect() throws IOException { // Konfigurationswerte holen final SntpConfiguration config = this.getNetTimeConfiguration(); short requestCount = config.getRequestCount(); if (requestCount <= 0) { return SystemClock.MONOTONIC.currentTime(); } String address = config.getTimeServerAddress(); int port = config.getTimeServerPort(); int timeout = config.getConnectionTimeout(); boolean version4 = config.isNTP4(); long pollInterval = config.getRequestInterval() * 1000; // lokaler Offset-Mittelwert in Mikrosekunden long sum = 0; long averageOffset = 0; // UDP-Socket öffnen DatagramSocket socket = null; try { socket = new DatagramSocket(); socket.setSoTimeout(timeout * 1000); for (int i = 1; i <= requestCount; i++) { // Zeitanfrage formulieren this.log(null, "Connecting NTP-Server, waiting for reply..."); InetAddress iaddr = InetAddress.getByName(address); SntpMessage requestMessage = new SntpMessage(version4); double transmitTS = requestMessage.getTransmitTimestamp(); byte version = requestMessage.getVersion(); byte[] data = requestMessage.getBytes(); // Zeitanfrage abschicken DatagramPacket request = new DatagramPacket(data, data.length, iaddr, port); socket.send(request); // Antwort abwarten DatagramPacket reply = new DatagramPacket(data, data.length); socket.receive(reply); // Sofort eigenen Timestamp notieren double destinationTimestamp = SntpMessage.getLocalTimestamp(); // Antwort auspacken SntpMessage replyMessage = new SntpMessage(reply.getData(), transmitTS, version); this.lastReply = replyMessage; if (this.isLogEnabled()) { this.log("NTP-Server connected: ", replyMessage.toString()); } // Annahme gleicher Netzlaufzeiten für Anfrage und Antwort // round-trip-delay: (D - O) - (T - R) = 2 * Netzlaufzeit // REAL-LOCAL-TIME = T + Netzlaufzeit = D + localClockOffset double localClockOffset = ( replyMessage.getReceiveTimestamp() - replyMessage.getOriginateTimestamp() + replyMessage.getTransmitTimestamp() - destinationTimestamp ) / 2.0; sum += (localClockOffset * MIO); averageOffset = (sum / i); if (replyMessage.getStratum() == 0) { this.log("NTP-Server replied: ", ""); break; } else if ( (i > 1) && (i < requestCount) ) { try { Thread.sleep(pollInterval); } catch (InterruptedException ie) { this.log(null, "NTP-Connection interrupted."); break; } } } } finally { if (socket != null) { socket.close(); } } long micros = SystemClock.MONOTONIC.currentTimeInMicros() + averageOffset; long seconds = micros / MIO; int nanosecond = (int) ((micros % MIO) * 1000); byte leapIndicator = this.lastReply.getLeapIndicator(); if (leapIndicator == 3) { throw new IOException( "Alarm condition: " + "NTP-Server is not synchronized with any clock source."); } else if (leapIndicator == 1) { this.log(null, "Positive leap second announced."); } else if (leapIndicator == 2) { this.log(null, "Negative leap second announced."); } return Moment.of(seconds, nanosecond, TimeScale.POSIX); } @Override protected SntpConfiguration loadNetTimeConfiguration() { final SntpConfiguration sc = super.loadNetTimeConfiguration(); short rcount = sc.getRequestCount(); int rinterval = sc.getRequestInterval(); if ((rcount < 0) || (rcount >= 1000)) { throw new IllegalStateException("Wrong request count: " + rcount); } else if (rinterval <= 0) { throw new IllegalStateException("Wrong request interval."); } return sc; } @Override protected Class getConfigurationType() { return SntpConfiguration.class; } private static SntpConfiguration initConfiguration() { ServiceLoader sl = ServiceLoader.load(SntpConfiguration.class); for (SntpConfiguration cfg : sl) { if (cfg != null) { return cfg; } } throw new IllegalStateException("SNTP-configuration not found."); } //~ Innere Klassen ---------------------------------------------------- private static class SimpleNtpConfiguration implements SntpConfiguration { //~ Instanzvariablen ---------------------------------------------- private final String server; //~ Konstruktoren ------------------------------------------------- SimpleNtpConfiguration(String server) { super(); if (server == null) { throw new NullPointerException("Missing time server address."); } this.server = server; } //~ Methoden ------------------------------------------------------ @Override public String getTimeServerAddress() { return this.server; } @Override public int getTimeServerPort() { return 123; } @Override public int getConnectionTimeout() { return DEFAULT_CONNECTION_TIMEOUT; } @Override public boolean isNTP4() { return true; } @Override public int getRequestInterval() { return 60 * 4; } @Override public short getRequestCount() { return 1; } @Override public int getClockShiftWindow() { return 0; } @Override public String toString() { StringBuilder sb = new StringBuilder(); sb.append("SimpleNtpConfiguration:[server="); sb.append(this.server); sb.append(",port="); sb.append(this.getTimeServerPort()); sb.append(']'); return sb.toString(); } } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy