Tải bản đầy đủ - 0 (trang)
Example 3-9. Time-service client using DatagramChannel

Example 3-9. Time-service client using DatagramChannel

Tải bản đầy đủ - 0trang

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.

*

* @author Ron Hitchens (ron@ronsoft.com)

*/

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();

}

protected InetSocketAddress receivePacket (DatagramChannel channel,

ByteBuffer buffer)

throws Exception

{

buffer.clear();

// Receive an unsigned 32-bit, big-endian value

return ((InetSocketAddress) channel.receive (buffer));

}



116



// 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);

buffer.flip();

replies++;

printTime (longBuffer.getLong (0), sa);

if (replies == expect) {

System.out.println ("All packets answered");

break;

}



117



// 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 hostname

InetSocketAddress sa = new InetSocketAddress (arg, port);

// Validate that it has an address

if (sa.getAddress() == null) {

System.out.println ("Cannot resolve address: "

+ arg);

continue;

}



118



remoteHosts.add (sa);

}

}

// -------------------------------------------------------------public static void main (String [] argv)

throws Exception

{

TimeClient client = new TimeClient (argv);

client.sendRequests();

client.getReplies();

}

}



The program in Example 3-10 is an RFC 868 time server. This code answers requests

from the client in Example 3-9 and shows how a DatagramChannel binds to a

well-known port and then listens for requests from clients. This time server listens only

for datagram (UDP) requests. The rdate command available on most Unix and Linux

systems uses TCP to connect to an RFC 868 time service.

Example 3-10. DatagramChannel time server

package com.ronsoft.books.nio.channels;

import

import

import

import

import

import



java.nio.ByteBuffer;

java.nio.ByteOrder;

java.nio.channels.DatagramChannel;

java.net.SocketAddress;

java.net.InetSocketAddress;

java.net.SocketException;



/**

* Provide RFC 868 time service (http://www.ietf.org/rfc/rfc0868.txt).

* This code implements an RFC 868 listener to provide time

* service. The defined port for time service is 37. On most

* unix systems, root privilege is required to bind to ports

* below 1024. You can either run this code as root or

* provide another port number on the command line. Use

* "-p port#" with TimeClient if you choose an alternate port.

*

* 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.

*

* @author Ron Hitchens (ron@ronsoft.com)

*/

public class TimeServer

{

private static final int DEFAULT_TIME_PORT = 37;

private static final long DIFF_1900 = 2208988800L;

protected DatagramChannel channel;



119



public TimeServer (int port)

throws Exception

{

this.channel = DatagramChannel.open();

this.channel.socket().bind (new InetSocketAddress (port));

System.out.println ("Listening on port " + port

+ " for time requests");

}

public void listen() 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();

while (true) {

buffer.clear();

SocketAddress sa = this.channel.receive (buffer);

if (sa == null) {

continue;

// defensive programming

}

// Ignore content of received datagram per RFC 868

System.out.println ("Time request from " + sa);

buffer.clear();



// sets pos/limit correctly



// Set 64-bit value; slice buffer sees low 32 bits

longBuffer.putLong (0,

(System.currentTimeMillis() / 1000) + DIFF_1900);

this.channel.send (buffer, sa);

}

}

// -------------------------------------------------------------public static void main (String [] argv)

throws Exception

{

int port = DEFAULT_TIME_PORT;

if (argv.length > 0) {

port = Integer.parseInt (argv [0]);



120



}

try {

TimeServer server = new TimeServer (port);

server.listen();

} catch (SocketException e) {

System.out.println ("Can't bind to port " + port

+ ", try a different one");

}

}

}



3.6 Pipes

The java.nio.channels package includes a class named Pipe. A pipe, in the general

sense, is a conduit through which data can be passed in a single direction between two

entities. The notion of a pipe has long been familiar to users of Unix (and Unix-like)

operating systems. Pipes are used on Unix systems to connect the output of one process

to the input of another. The Pipe class implements a pipe paradigm, but the pipes it

creates are intraprocess (within the JVM process) rather than interprocess (between

processes). See Figure 3-10.

Figure 3-10. The Pipe family tree



121



The Pipe class creates a pair of Channel objects that provide a loopback mechanism. The

two channels' far ends are connected so that whatever is written down the SinkChannel

appears on the SourceChannel. Figure 3-11 shows the class hierarchy for Pipe.

package java.nio.channels;

public abstract class Pipe

{

public static Pipe open() throws IOException

public abstract SourceChannel source();

public abstract SinkChannel sink();



122



Tài liệu bạn tìm kiếm đã sẵn sàng tải về

Example 3-9. Time-service client using DatagramChannel

Tải bản đầy đủ ngay(0 tr)

×