package com.ronsoft.books.nio.channels;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.channels.DatagramChannel;
import java.net.InetSocketAddress;
import java.util.Date;
import java.util.List;
import java.util.LinkedList;
import java.util.Iterator;
/**
* Request time service, per RFC 868. RFC 868
* (http://www.ietf.org/rfc/rfc0868.txt) is a very simple time protocol
* whereby one system can request the current time from another system.
* Most Linux, BSD and Solaris systems provide RFC 868 time service
* on port 37. This simple program will inter-operate with those.
* The National Institute of Standards and Technology (NIST) operates
* a public time server at time.nist.gov.
*
* The RFC 868 protocol specifies a 32 bit unsigned value be sent,
* representing the number of seconds since Jan 1, 1900. The Java
* epoch begins on Jan 1, 1970 (same as unix) so an adjustment is
* made by adding or subtracting 2,208,988,800 as appropriate. To
* avoid shifting and masking, a four-byte slice of an
* eight-byte buffer is used to send/recieve. But getLong()
* is done on the full eight bytes to get a long value.
*
* When run, this program will issue time requests to each hostname
* given on the command line, then enter a loop to receive packets.
* Note that some requests or replies may be lost, which means
* this code could block forever.
*
* Created: April 2002
* @author Ron Hitchens (ron@ronsoft.com)
* @version $Id: TimeClient.java,v 1.2 2002/04/28 22:07:43 ron Exp $
*/
public class TimeClient
{
private static final int DEFAULT_TIME_PORT = 37;
private static final long DIFF_1900 = 2208988800L;
// --------------------------------------------------------------
protected int port = DEFAULT_TIME_PORT;
protected List remoteHosts;
protected DatagramChannel channel;
public TimeClient (String [] argv) throws Exception
{
if (argv.length == 0) {
throw new Exception ("Usage: [ -p port ] host ...");
}
parseArgs (argv);
this.channel = DatagramChannel.open();
}
// this version never times out
protected InetSocketAddress receivePacket (DatagramChannel channel,
ByteBuffer buffer)
throws Exception
{
buffer.clear();
// receive an unsigned 32-bit, big-endian value
return ((InetSocketAddress) channel.receive (buffer));
}
// send time requests to all the supplied hosts
protected void sendRequests()
throws Exception
{
ByteBuffer buffer = ByteBuffer.allocate (1);
Iterator it = remoteHosts.iterator();
while (it.hasNext()) {
InetSocketAddress sa = (InetSocketAddress) it.next();
System.out.println ("Requesting time from "
+ sa.getHostName() + ":" + sa.getPort());
// make it empty, see RFC868
buffer.clear().flip();
// fire and forget
channel.send (buffer, sa);
}
}
// receive any replies that arrive
public void getReplies() throws Exception
{
// allocate a buffer to hold a long value
ByteBuffer longBuffer = ByteBuffer.allocate (8);
// assure big-endian (network) byte order
longBuffer.order (ByteOrder.BIG_ENDIAN);
// zero the whole buffer to be sure
longBuffer.putLong (0, 0);
// position to first byte of the low-order 32 bits
longBuffer.position (4);
// slice the buffer, gives view of the low-order 32 bits
ByteBuffer buffer = longBuffer.slice();
int expect = remoteHosts.size();
int replies = 0;
System.out.println ("");
System.out.println ("Waiting for replies...");
while (true) {
InetSocketAddress sa;
sa = receivePacket (channel, buffer);
if (sa == null) {
// only get here if receivePacket timed out
System.out.println ("Time's up, "
+ (expect - replies)
+ " replies never arrived");
break;
}
buffer.flip();
replies++;
printTime (longBuffer.getLong (0), sa);
if (replies == expect) {
System.out.println ("All packets answered");
break;
}
// some replies haven't shown up yet
System.out.println ("Received " + replies
+ " of " + expect + " replies");
}
}
// print info about a received time reply
protected void printTime (long remote1900, InetSocketAddress sa)
{
// local time as seconds since Jan 1, 1970
long local = System.currentTimeMillis() / 1000;
// remote time as seconds since Jan 1, 1970
long remote = remote1900 - DIFF_1900;
Date remoteDate = new Date (remote * 1000);
Date localDate = new Date (local * 1000);
long skew = remote - local;
System.out.println ("Reply from "
+ sa.getHostName() + ":" + sa.getPort());
System.out.println (" there: " + remoteDate);
System.out.println (" here: " + localDate);
System.out.print (" skew: ");
if (skew == 0) {
System.out.println ("none");
} else if (skew > 0) {
System.out.println (skew + " seconds ahead");
} else {
System.out.println ((-skew) + " seconds behind");
}
}
protected void parseArgs (String [] argv)
{
remoteHosts = new LinkedList();
for (int i = 0; i < argv.length; i++) {
String arg = argv [i];
// send client requests to the given port
if (arg.equals ("-p")) {
i++;
this.port = Integer.parseInt (argv [i]);
continue;
}
// create an address object for the host name
InetSocketAddress sa = new InetSocketAddress (arg, port);
// validate that it has an address
if (sa.getAddress() == null) {
System.out.println ("Cannot resolve address: "
+ arg);
continue;
}
remoteHosts.add (sa);
}
}
// --------------------------------------------------------------
public static void main (String [] argv)
throws Exception
{
TimeClient client = new TimeClient (argv);
client.sendRequests();
client.getReplies();
}
}
|