OpenConcerto

Dépôt officiel du code source de l'ERP OpenConcerto
sonarqube

svn://code.openconcerto.org/openconcerto

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 2011 OpenConcerto, by ILM Informatique. All rights reserved.
 * 
 * The contents of this file are subject to the terms of the GNU General Public License Version 3
 * only ("GPL"). You may not use this file except in compliance with the License. You can obtain a
 * copy of the License at http://www.gnu.org/licenses/gpl-3.0.html See the License for the specific
 * language governing permissions and limitations under the License.
 * 
 * When distributing the software, include this License Header Notice in each file.
 */
 
 package org.openconcerto.utils.ntp;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.SocketException;

public class SNTPClient {

    protected final DatagramSocket socket;

    /**
     * The SNTP time is referenced to 01/01/1900-00:00. On the other hand, Unix systems and Java
     * reference time to 01/01/1970-00:00. This means that convertion is necessary.
     */
    private static final long SECS_1900_1970 = 2208988800L;

    /**
     * Constructor.
     * 
     * @param timeout the response timeout (in milliseconds).
     * @throws IllegalArgumentException if the timeout is invalid.
     * @throws SocketException if an error occurs while creating the socket.
     */
    public SNTPClient() throws SocketException {
        this.socket = new DatagramSocket();
        // 10s timeout
        socket.setSoTimeout(10000);
    }

    /**
     * Retrieves the network time offset from an SNTP server.
     * 
     * @param host the server host address (IP or DNS).
     * @param port the server port.
     * @return the network time offset (in milliseconds).
     * 
     * @throws IOException if an error occurs while contacting the server.
     */
    public long getOffsetInMillis(final String host, final int port) throws IOException {
        final InetAddress addr = InetAddress.getByName(host);
        // Send
        final Message smessage = new Message();
        smessage.setTransmitTimestamp(toTimestamp(System.currentTimeMillis()));
        final ByteArrayOutputStream output = new ByteArrayOutputStream();
        smessage.encodeMessage(output);
        final byte[] data = output.toByteArray();
        output.close();

        DatagramPacket rpacket = new DatagramPacket(data, data.length, addr, port);
        socket.send(rpacket);

        // Receive
        final DatagramPacket packet = new DatagramPacket(new byte[Message.MAXIMUM_LENGTH], Message.MAXIMUM_LENGTH);
        socket.receive(packet);
        final long destinationTime = System.currentTimeMillis();
        final Message rmessage = Message.decodeMessage(new ByteArrayInputStream(packet.getData()));
        final long originalTime = fromTimestamp(rmessage.getOriginateTimestamp());
        final long receiveTime = fromTimestamp(rmessage.getReceiveTimestamp());
        final long transmitTime = fromTimestamp(rmessage.getTransmitTimestamp());
        return ((receiveTime - originalTime) + (transmitTime - destinationTime)) / 2;

    }

    public void close() {
        socket.close();
    }

    /**
     * Converts Java time to an SNTP timestamp.
     * 
     * @param time the Java time (in milliseconds).
     * @return the SNTP timestamp.
     */
    private static Timestamp toTimestamp(final long time) {
        final double temp = ((double) time) / 1000D;
        final long integer = (long) Math.floor(temp);
        final long fraction = (long) ((temp - (double) integer) * 0x100000000L);
        return new Timestamp(integer + SECS_1900_1970, fraction);
    }

    /**
     * Converts an SNTP timestamp to Java time.
     * 
     * @param timestamp the timestamp.
     * @return the Java time (in milliseconds).
     */
    private static final long fromTimestamp(final Timestamp timestamp) {
        long time = (timestamp.getInteger() - SECS_1900_1970) * 1000L;
        time += (timestamp.getFraction() * 1000L) / 0x100000000L;
        return time;
    }

    public static void main(String[] args) throws IOException {
        final SNTPClient client = new SNTPClient();
        final long offset = client.getOffsetInMillis("fr.pool.ntp.org", 123);
        client.close();
        System.out.println(offset);
    }
}