Reputation: 870
Let's say we have a Java NIO Selector
that selects with a timeout on multiple SocketChannels
for read operations:
Selector selector = Selector.open();
channel1.register(selector, SelectionKey.OP_READ);
channel2.register(selector, SelectionKey.OP_READ);
channel3.register(selector, SelectionKey.OP_READ);
channel4.register(selector, SelectionKey.OP_READ);
// ... maybe even more ...
while (true) {
if (selector.select(TIMEOUT) > 0) {
Iterator<SelectionKey> iterator = selector.selectedKeys().iterator();
while (iterator.hasNext()) {
SelectionKey key = iterator.next();
if (key.isValid() && key.isReadable())
read(key);
iterator.remove();
}
} else {
// All channels timed-out! Cancel and close them all
cancelAndCloseAll(selector.keys());
}
}
We need to cancel and close a channel if it's been idle for a specific time, and that's why we're using the selector.select(TIMEOUT)
method.
But this doesn't work if we have a few very active channels. Those active channels will never let the select
to timeout, while all the other channels might be idle ...
A naive solution for this is as follows (also mentioned here) :
Attach the last time of activity to each channel's
SelectionKey
using thekey.attach(object)
method. After each successful select, update the activity time of all ready keys. Then iterate over all the keys and find the ones that have been idle more than the specific threshold.
This could be really inefficient because the active channels will cause the select
to fire very frequently and everytime iterating all over the keys set.
So is there a better (more efficient) way of solving this problem ?
Upvotes: 1
Views: 1525
Reputation: 5055
You could adjust your naive solution:
Upvotes: 1
Reputation: 311023
Use a Java.util.Timer, and submit a TimerTask that closes the channel after the idle period. Cancel the task if it exists and submit a new one when you get activity once channel before it runs. Save the TimerTasks somewhere you can find them by channel, e,g, in a session object stored as the key attachment, which would also hold the channel's ByteBuffer(s), user ID, etc, whatever else you need per session.
You'll run into concurrency issues that will require you to either wakeup the selector in the timer task, and have the selector thread cope correctly with being awoken when nothing is ready (select() returns zero), or put up with channels not being closed exactly on time, due to the timer task blocking in close() while the selector is blocking in select().
Upvotes: 0