/*
* Copyright (c) 2004 David Flanagan. All rights reserved.
* This code is from the book Java Examples in a Nutshell, 3nd Edition.
* It is provided AS-IS, WITHOUT ANY WARRANTY either expressed or implied.
* You may study, use, and modify it for any non-commercial purpose,
* including teaching and use in open-source projects.
* You may distribute it non-commercially as long as you retain this notice.
* For a commercial use license, or to purchase the book,
* please visit http://www.davidflanagan.com/javaexamples3.
*/
//package je3.nio;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
import java.nio.channels.Channel;
import java.nio.channels.DatagramChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetEncoder;
import java.util.Iterator;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A more robust daytime service, that handles TCP and UDP connections and
* provides exception handling and error logging.
*/
public class DaytimeServer {
public static void main(String args[]) {
try { // Handle startup exceptions at the end of this block
// Get an encoder for converting strings to bytes
CharsetEncoder encoder = Charset.forName("US-ASCII").newEncoder();
// Allow an alternative port for testing with non-root accounts
int port = 13; // RFC867 specifies this port.
if (args.length > 0)
port = Integer.parseInt(args[0]);
// The port we'll listen on
SocketAddress localport = new InetSocketAddress(port);
// Create and bind a tcp channel to listen for connections on.
ServerSocketChannel tcpserver = ServerSocketChannel.open();
tcpserver.socket().bind(localport);
// Also create and bind a DatagramChannel to listen on.
DatagramChannel udpserver = DatagramChannel.open();
udpserver.socket().bind(localport);
// Specify non-blocking mode for both channels, since our
// Selector object will be doing the blocking for us.
tcpserver.configureBlocking(false);
udpserver.configureBlocking(false);
// The Selector object is what allows us to block while waiting
// for activity on either of the two channels.
Selector selector = Selector.open();
// Register the channels with the selector, and specify what
// conditions (a connection ready to accept, a datagram ready
// to read) we'd like the Selector to wake up for.
// These methods return SelectionKey objects, which we don't
// need to retain in this example.
tcpserver.register(selector, SelectionKey.OP_ACCEPT);
udpserver.register(selector, SelectionKey.OP_READ);
// This is an empty byte buffer to receive emtpy datagrams with.
// If a datagram overflows the receive buffer size, the extra bytes
// are automatically discarded, so we don't have to worry about
// buffer overflow attacks here.
ByteBuffer receiveBuffer = ByteBuffer.allocate(0);
// Now loop forever, processing client connections
for (;;) {
try { // Handle per-connection problems below
// Wait for a client to connect
selector.select();
// If we get here, a client has probably connected, so
// put our response into a ByteBuffer.
String date = new java.util.Date().toString() + "\r\n";
ByteBuffer response = encoder.encode(CharBuffer.wrap(date));
// Get the SelectionKey objects for the channels that have
// activity on them. These are the keys returned by the
// register() methods above. They are returned in a
// java.util.Set.
Set keys = selector.selectedKeys();
// Iterate through the Set of keys.
for (Iterator i = keys.iterator(); i.hasNext();) {
// Get a key from the set, and remove it from the set
SelectionKey key = (SelectionKey) i.next();
i.remove();
// Get the channel associated with the key
Channel c = (Channel) key.channel();
// Now test the key and the channel to find out
// whether something happend on the TCP or UDP channel
if (key.isAcceptable() && c == tcpserver) {
// A client has attempted to connect via TCP.
// Accept the connection now.
SocketChannel client = tcpserver.accept();
// If we accepted the connection successfully,
// the send our respone back to the client.
if (client != null) {
client.write(response); // send respone
client.close(); // close connection
}
} else if (key.isReadable() && c == udpserver) {
// A UDP datagram is waiting. Receive it now,
// noting the address it was sent from.
SocketAddress clientAddress = udpserver.receive(receiveBuffer);
// If we got the datagram successfully, send
// the date and time in a response packet.
if (clientAddress != null)
udpserver.send(response, clientAddress);
}
}
} catch (java.io.IOException e) {
// This is a (hopefully transient) problem with a single
// connection: we log the error, but continue running.
// We use our classname for the logger so that a sysadmin
// can configure logging for this server independently
// of other programs.
Logger l = Logger.getLogger(DaytimeServer.class.getName());
l.log(Level.WARNING, "IOException in DaytimeServer", e);
} catch (Throwable t) {
// If anything else goes wrong (out of memory, for example)
// then log the problem and exit.
Logger l = Logger.getLogger(DaytimeServer.class.getName());
l.log(Level.SEVERE, "FATAL error in DaytimeServer", t);
System.exit(1);
}
}
} catch (Exception e) {
// This is a startup error: there is no need to log it;
// just print a message and exit
System.err.println(e);
System.exit(1);
}
}
}
|