Using Selector to wait for events (example with OP_CONNECT)
NIO appeared in Java 1.4 and introduced the concept of "Channels", which are supposed to be faster than regular I/O. Network-wise, the SelectableChannel(opens new window) is the most interesting as it allows to monitor different states of the Channel. It works in a similar manner as the C select() system call: we get woken-up when certain types of events occur:
connection received (OP_ACCEPT)
connection realized (OP_CONNECT)
data available in read FIFO (OP_READ)
data can be pushed to write FIFO (OP_WRITE)
It allows for separation between detecting socket I/O (something can be read/written/...) and performing the I/O (read/write/...). Especially, all I/O detection can be done in a single thread for multiple sockets (clients), while performing I/O can be handled in a thread pool or anywhere else. That allows for an application to scale easily to the number of connected clients.
Selector sel =Selector.open();// Create the SelectorSocketChannel sc =SocketChannel.open();// Create a SocketChannel
sc.configureBlocking(false);// ... non blocking
sc.setOption(StandardSocketOptions.SO_KEEPALIVE,true);// ... set some options// Register the Channel to the Selector for wake-up on CONNECT event and use some description as an attachement
sc.register(sel,SelectionKey.OP_CONNECT,"Connection to google.com");// Returns a SelectionKey: the association between the SocketChannel and the SelectorSystem.out.println("Initiating connection");if(sc.connect(newInetSocketAddress("www.google.com",80)))System.out.println("Connected");// Connected right-away: nothing else to doelse{boolean exit =false;while(!exit){if(sel.select(100)==0)// Did something happen on some registered Channels during the last 100ms?continue;// No, wait some more// Something happened...Set<SelectionKey> keys = sel.selectedKeys();// List of SelectionKeys on which some registered operation was triggeredfor(SelectionKey k : keys){System.out.println("Checking "+k.attachment());if(k.isConnectable()){// CONNECT eventSystem.out.print("Connected through select() on "+k.channel()+" -> ");if(sc.finishConnect()){// Finish connection processSystem.out.println("done!");
k.interestOps(k.interestOps()&~SelectionKey.OP_CONNECT);// We are already connected: remove interest in CONNECT event
exit =true;}elseSystem.out.println("unfinished...");}// TODO: else if (k.isReadable()) { ...}
keys.clear();// Have to clear the selected keys set once processed!}}System.out.print("Disconnecting ... ");
sc.shutdownOutput();// Initiate graceful disconnection// TODO: emtpy receive buffer
sc.close();System.out.println("done");
Would give the following output:
Initiating connection
CheckingConnectiontogoogle.comConnected through 'select()' on java.nio.channels.SocketChannel[connection-pending remote=www.google.com/216.58.208.228:80]-> done!Disconnecting... done
Handling OP_READ is pretty straight-forward. However, care should be taken when dealing with OP_WRITE: most of the time, data can be written to sockets so the event will keep firing. Make sure to register OP_WRITE only before you want to write data (see that answer(opens new window)).
Also, OP_CONNECT should be cancelled once the Channel has connected (because, well, it is connected. See this(opens new window) and that(opens new window) answers on SO). Hence the OP_CONNECT removal after finishConnect() succeeded.