/** Java NIO - Ron Hitchens, O'Reilly Media - with minor rewrites*/ import java.nio.ByteBuffer; import java.nio.channels.ServerSocketChannel; import java.nio.channels.SocketChannel; import java.nio.channels.Selector; import java.nio.channels.SelectionKey; import java.nio.channels.SelectableChannel; import java.net.Socket; import java.net.ServerSocket; import java.net.InetSocketAddress; import java.util.Iterator; /** * Simple echo-back server which listens for incoming stream connections * and echoes back whatever it reads. A single Selector object is used to * listen to the server socket (to accept new connections) and all the * active socket channels. * * @author Ron Hitchens (ron@ronsoft.com) */ public class SelectSockets { public static int PORT_NUMBER = 1234; public static void main (String [] argv) throws Exception { new SelectSockets( ).go (argv); } public void go (String [] argv) throws Exception { int port = PORT_NUMBER; if (argv.length > 0) { // Override default listen port port = Integer.parseInt (argv [0]); } System.out.println ("Listening on port " + port); // Allocate an unbound server socket channel ServerSocketChannel serverChannel = ServerSocketChannel.open( ); // Get the associated ServerSocket to bind it with ServerSocket serverSocket = serverChannel.socket( ); // Create a new Selector for use below Selector selector = Selector.open( ); // Set the port the server channel will listen to serverSocket.bind (new InetSocketAddress (port)); // Set nonblocking mode for the listening socket serverChannel.configureBlocking (false); // Register the ServerSocketChannel with the Selector serverChannel.register (selector, SelectionKey.OP_ACCEPT); while (true) { // This may block for a long time. Upon returning, the // selected set contains keys of the ready channels. int n = selector.select( ); if (n == 0) { continue; // nothing to do } // Get an iterator over the set of selected keys Iterator it = selector.selectedKeys().iterator( ); // Look at each key in the selected set while (it.hasNext( )) { SelectionKey key = (SelectionKey) it.next( ); // Is a new connection coming in? if (key.isAcceptable( )) { // we know this key is associated with serverChannel // so we do not need to do // ServerSocketChannel server = // (ServerSocketChannel) key.channel( ); SocketChannel channel = serverChannel.accept( ); if (channel != null) { // Set the new channel nonblocking channel.configureBlocking (false); // Register it with the selector channel.register (selector, SelectionKey.OP_READ); sayHello (channel); } } // Is there data to read on this channel? if (key.isReadable( )) { readDataFromSocket (key); } // Remove key from selected set; it's been handled it.remove( ); } } } // ---------------------------------------------------------- // Use the same byte buffer for all channels. A single thread is // servicing all the channels, so no danger of concurrent acccess. private ByteBuffer buffer = ByteBuffer.allocateDirect (1024); /** * Sample data handler method for a channel with data ready to read. * @param key A SelectionKey object associated with a channel * determined by the selector to be ready for reading. If the * channel returns an EOF condition, it is closed here, which * automatically invalidates the associated key. The selector * will then de-register the channel on the next select call. */ protected void readDataFromSocket (SelectionKey key) throws Exception { SocketChannel socketChannel = (SocketChannel) key.channel( ); int count; buffer.clear( ); // Empty buffer // Loop while data is available; channel is nonblocking while ((count = socketChannel.read (buffer)) > 0) { buffer.flip( ); // Make buffer readable // Send the data; don't assume it goes all at once while (buffer.hasRemaining( )) { socketChannel.write (buffer); } // WARNING: the above loop is evil. Because // it's writing back to the same nonblocking // channel it read the data from, this code can // potentially spin in a busy loop. In real life // you'd do something more useful than this. buffer.clear( ); // Empty buffer } if (count < 0) { // Close channel on EOF, invalidates the key socketChannel.close( ); } } // ---------------------------------------------------------- /** * Spew a greeting to the incoming client connection. * @param channel The newly connected SocketChannel to say hello to. */ private void sayHello (SocketChannel channel) throws Exception { buffer.clear( ); buffer.put ("Hi there!\r\n".getBytes( )); buffer.flip( ); channel.write (buffer); } }