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;
// This code is deprecated, see TimeClient and TimeServer
/**
* Request and/or provide 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.
* It's a very simple protocol, specified nearly 20 years ago, which
* does not take into account packet transit time or precision of less
* than one second. The Network Time Protocol (NTP) is superior but
* far too complex for demonstration purposes.
* 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.
*
* This code also implements an RFC 868 listener to provide time
* service. On most unix systems, root privileges are required to
* bind to ports below 1024. You can either run this code as root
* or provide another port number with "-l port#". If you run this
* server on an alternate port, you can connect from another JVM
* by supplying the "-p" option to specify the alternate port for
* requests.
*
* Note: The familiar rdate command on unix will probably not work
* with this server. Most versions of rdate use TCP rather than UDP
* to request the time.
*
* When run, this program will issue time requests to each hostname
* given on the command line, then enter a loop to receive packets.
* If not acting as a server, it will exit when all expected replies
* have arrived. Note that some replies may be lost, which means
* this code may block forever. If acting as a server, time requests
* from remote systems will be dispatched as well, in the same loop.
*
* 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 casting shifting an masking, a four-byte slice of an
* eight-byte buffer is used send/recieve, but then get/putLong()
* is done on the full eight bytes to get a long value.
*
* Created: April 2002
* @author Ron Hitchens (ron@ronsoft.com)
* @version $Id: TimeService.java,v 1.4 2002/05/20 07:24:29 ron Exp $
*/
public class TimeService
{
private static final int DEFAULT_TIME_PORT = 37;
private static final long DIFF_1900 = 2208988800L;
// --------------------------------------------------------------
protected DatagramChannel channel;
protected boolean listening = false;
public TimeService (int port)
throws Exception
{
this.channel = DatagramChannel.open();
if (port != 0) {
System.out.println ("Listening on port " + port
+ " for time requests");
channel.socket().bind (new InetSocketAddress (port));
listening = true;
}
}
// this version never times out
protected InetSocketAddress receivePacket (DatagramChannel channel,
ByteBuffer buffer)
throws Exception
{
buffer.clear();
// receive a 32-bit, big-endian, unsigned value
return ((InetSocketAddress) channel.receive (buffer));
}
public void run (int expect)
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 replies = 0;
while (true) {
InetSocketAddress sa;
sa = receivePacket (channel, buffer);
if (sa == null) {
// only get here if receivePacket timed out
if (listening) {
continue;
}
System.out.println ("Time's up, "
+ (expect - replies)
+ " replies never arrived");
break;
}
buffer.flip();
if (buffer.remaining() == 0) {
// empty packet, someone requesting our time
System.out.println ("Time request from "
+ hostInfo (sa));
serveTimeRequest (sa);
} else {
// reply to a time request we sent
replies++;
printTime (longBuffer.getLong (0), sa);
}
if (expect == 0) {
// running as pure server, carry on
continue;
}
if (replies == expect) {
// all request were answered
System.out.println ("All packets answered");
expect = 0;
if ( ! listening) {
break;
}
} else {
// some haven't shown up yet
System.out.println ("Received " + replies
+ " of " + expect + " replies");
}
}
}
// send a time request to the given internet address
public void requestTime (InetSocketAddress sa)
throws Exception
{
ByteBuffer buffer = ByteBuffer.allocate (1);
buffer.flip(); // make it empty, see RFC868
// fire and forget
channel.send (buffer, sa);
}
// print info about a received time reply
protected void printTime (long remote1900, InetSocketAddress sa)
{
long remote = remote1900 - DIFF_1900;
long local = currentTimeSecs();
Date remoteDate = new Date (remote * 1000);
Date localDate = new Date (local * 1000);
long skew = remote - local;
System.out.println ("Reply from " + hostInfo (sa));
System.out.println (" there: " + remoteDate);
System.out.println (" here: " + localDate);
System.out.print (" skew: ");
if (skew == 0) {
System.out.println ("none");
} else {
System.out.println (skew + " seconds "
+ ((skew < 0) ? "behind" : "ahead"));
}
}
// send the local time to the remote requestor
protected void serveTimeRequest (InetSocketAddress sa)
throws Exception
{
ByteBuffer buffer = ByteBuffer.allocate (8);
buffer.clear();
buffer.putLong (0, currentTimeSecs() + DIFF_1900);
buffer.position (4); // low-order 32-bit int
this.channel.send (buffer, sa);
}
// get the local time as seconds since Jan 1, 1970
public static long currentTimeSecs()
{
return (System.currentTimeMillis() / 1000); // secs/1970
}
// convenience formatting method
public static String hostInfo (InetSocketAddress sa)
{
return (sa.getHostName() + ":" + sa.getPort());
}
// --------------------------------------------------------------
public static TimeService newInstance (int listenPort)
throws Exception
{
return (new TimeService (listenPort));
}
public static void main (String [] argv)
throws Exception
{
if (argv.length == 0) {
System.out.println ("Usage: [ -s | -l port ] [ -p port ] host ...");
return;
}
List hosts = new LinkedList();
int listenPort = 0;
int port = DEFAULT_TIME_PORT;
for (int i = 0; i < argv.length; i++) {
String arg = argv [i];
// listen as a server, on the default port
if (arg.equals ("-s")) {
listenPort = DEFAULT_TIME_PORT;
continue;
}
// listen as a server, on the given port
if (arg.equals ("-l")) {
i++;
listenPort = Integer.parseInt (argv [i]);
continue;
}
// send client requests to the given port
if (arg.equals ("-p")) {
i++;
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;
}
hosts.add (sa);
}
TimeService timeService = newInstance (listenPort);
// send time requests to all the supplied hosts
Iterator it = hosts.iterator();
while (it.hasNext()) {
InetSocketAddress sa = (InetSocketAddress) it.next();
System.out.println ("Requesting time from "
+ hostInfo (sa));
timeService.requestTime (sa);
}
System.out.println ("");
System.out.println ("Waiting for replies...");
timeService.run (hosts.size());
}
}
|