Download (direct link):
1. At line 43, we create a Selector object (by calling the Selector.open() method) to multiplex all registered channels.
2. At line 47, we register the SelectableChannel with the Selector and specify which operations in the channel we want to be notified about. Here lies are first potential pitfall. If we do not register the correct operations for this channel object (which is provided by a Service Provider and not instantiated), an exception will be thrown. The operations that you can register for are OP_ACCEPT, OP_CONNECT, OP_READ, and OP_WRITE. In fact, in Listing 2.4, since we did not check which operations are valid on this channel with the validOps() method, an exception will be thrown.
3. At line 52, we call select() on the Selector to wait for operations on the registered channels.
4. At line 55, the select() method returns the number of SelectionKeys ready for processing. If the number is greater than 0, you can retrieve the set of selected SelectionKeys. A SelectionKey represents the registration and state of a Channel registered with a Selector. Once you have the SelectionKey, you can test its state and act accordingly.
Running Listing 2.4 produces:
Received a: sun.nio.ch.ServerSocketChannelImpl
The IllegalArgumentException is thrown because we attempted to register operations that were not valid on the ServerSocketChannel. The only operation we can register on that channel is OP_ACCEPT. Listing 2.5 registers the correct operation,
32 Item 2
accepts the channel, and receives a file from the client. Listing 2.5 presents the changes to the acceptConnections() method.
025 public void acceptConnections(int port) throws Exception
// . .. omitted code Identical to Listing 2.4
053 SelectionKey theKey = ssc.register(theSelector, 2
055 int readyKeyCnt = 0;
056 while ( (readyKeyCnt = theSelector.select()) > 0)
058 System.out.println("Have " + readyKeyCnt + " ready keys... );
060 // get the ready keys
061 Set readyKeys = theSelector.selectedKeys();
062 Iterator i = readyKeys.iterator();
064 // Walk through the ready keys collection and process the 2
065 while (i.hasNext())
067 // get the key
068 SelectionKey sk = (SelectionKey)i.next();
071 if (sk.isAcceptable())
073 System.out.println("is Acceptable.");
074 // accept it
076 // get the channel
077 ServerSocketChannel channel = (ServerSocketChannel) 2
079 // using method in NBTimeServer JDK example
080 System.out.println("Accepting the connection.");
081 Socket s = channel.accept().socket();
083 DatalnputStream dis = new 2
084 DataOutputStream dos = new
086 // Code to read file from the client ...
Listing 2.5 Changes to acceptConnections() method
NIO Performance and Pitfalls 33
After working our way through the simple incorrect event registration pitfall, we can create a non-blocking server that properly accepts a socket connection. Here are the key changes highlighted in Listing 2.5:
¦¦ At line 53, we register the single operation OP_ACCEPT on the server socket channel.
¦¦ At line 56, we call select() to wait on any events on the registered channel.
¦¦ At line 69, we remove the SelectionKey from the set of SelectionKeys returned from the select() method. This is a potential pitfall, because if you do not remove the key, you will reprocess it. So, it is the programmer's responsibility to remove it from the set. This is especially dangerous if you have multiple threads processing the selection keys.
¦¦ At line 71, we test if the key isAcceptable() ,which is the only operation we registered for. However, it is important to understand that once accepted, you get a channel for each incoming connection (each a separate key), which can in turn be registered with the Selector for other operations (reads and writes).
¦¦ At line 77, we get the registered channel (in this case the ServerSock-etChannel) from the SelectionKey via the channel() method.
¦¦ At line 81, we call the accept() method to accept the socket connection and get a SocketChannel object. Given this object we can either process the channel (which is the approach of our simple server) or register it with the Selector like this:
SocketChannel sockChannel = channel.accept(); sockChannel.configureBlocking( false );
SelectionKey readKey =
sockChannel.register( theSelector, SelectionKey.OP_READ|SelectionKey.OP_WRITE );
A run of Listing 2.5 (ImageAnnotationServer2) accepts a single connection, receives the file, and then exits. The problem is in line 56 where the while loop (which follows Sun's NBTimeServer example) only continues if there are greater than 0 events returned from select(); however, the documentation clearly states that 0 events may be returned. Therefore to fix this pitfall, it is necessary to loop forever in the server and not assume select() will block until at least one event is ready, like this: