Using a Selector to Manage Non-Blocking Sockets
Although you could poll each non-blocking socket for events, a more
convenient and efficient method is to use a selector to manage the
channels. The selector efficiently monitors the channels for changes
and reports the events through a single method call.
The first step is to register a channel with a selector. The
registration process yields an object called a selection key which
identifies the selector/socket channel pair (a channel could be
registered with another selector for different events). When an event
occurs on a channel, the selector returns the selection key for that
channel. The selection key also contains the type of event that
occurred.
This example creates two sockets and registers them with
a selector. The example then uses the selector to listen for events.
See also Using a Selector to Manage Non-Blocking Server Sockets.
// Create a selector and register two socket channels
Selector selector = null;
try {
// Create the selector
selector = Selector.open();
// Create two non-blocking sockets. This method is implemented in
// Creating a Non-Blocking Socket.
SocketChannel sChannel1 = createSocketChannel("hostname.com", 80);
SocketChannel sChannel2 = createSocketChannel("hostname.com", 80);
// Register the channel with selector, listening for all events
sChannel1.register(selector, sChannel1.validOps());
sChannel2.register(selector, sChannel1.validOps());
} catch (IOException e) {
}
// Wait for events
while (true) {
try {
// Wait for an event
selector.select();
} catch (IOException e) {
// Handle error with selector
break;
}
// Get list of selection keys with pending events
Iterator it = selector.selectedKeys().iterator();
// Process each key at a time
while (it.hasNext()) {
// Get the selection key
SelectionKey selKey = (SelectionKey)it.next();
// Remove it from the list to indicate that it is being processed
it.remove();
try {
processSelectionKey(selKey);
} catch (IOException e) {
// Handle error with channel and unregister
selKey.cancel();
}
}
}
public void processSelectionKey(SelectionKey selKey) throws IOException {
// Since the ready operations are cumulative,
// need to check readiness for each operation
if (selKey.isValid() && selKey.isConnectable()) {
// Get channel with connection request
SocketChannel sChannel = (SocketChannel)selKey.channel();
boolean success = sChannel.finishConnect();
if (!success) {
// An error occurred; handle it
// Unregister the channel with this selector
selKey.cancel();
}
}
if (selKey.isValid() && selKey.isReadable()) {
// Get channel with bytes to read
SocketChannel sChannel = (SocketChannel)selKey.channel();
// See Reading from a SocketChannel
}
if (selKey.isValid() && selKey.isWritable()) {
// Get channel that's ready for more bytes
SocketChannel sChannel = (SocketChannel)selKey.channel();
// See Writing to a SocketChannel
}
}
Hello,
Thank you for the nicely-written code. It has helped me write my own socket code. A small fix on line 14 of the above example:
sChannel2.register(selector, sChannel1.validOps());
maybe should be changed to
sChannel2.register(selector, sChannel2.validOps());
I know it comes out the same either way.
Thanks again!
Volt
How does for example a "write-event" look like?
How can I say: "SocketChannel X write this ByteArray[]!" ?
greetings
How does for example a "write-event" look like?
How can I say: "SocketChannel X write this ByteArray[]!" ?
greetings
Thanks for introducing a little rationality into this dbetae.