If you use Selectors and non-blocking IOs, you might consider allowing NIOs to independently monitor the link between the channel and state data. When you call SelectionKey.register (), you can use a form with three arguments to pass the "attachment". At every moment in the future, this SelectionKey will always return the binding object that you provided. (This is pretty clearly inspired by an argument like "void * user_data" in the level APIs.)
This application stays with the key, so it is a convenient place to store status data. The best part is that all the mapping from the channel to the key to the application will already be processed by NIO, so you do less bookkeeping. Bookkeeping - for example, map search - can really damage the IO's internal response loop.
As an additional feature, you can also change the attachment later, so if you need different state objects for different phases of your protocol, you can also track this in SelectionKey.
As for the odd state in which you find your connections, there are some subtleties to using NIO and selectors that may bite you. For example, as soon as the SelectionKey signals that it is ready to read, it will continue to be ready to read the next time some other calls to the select () threads. Thus, it is easy to terminate multiple threads while trying to read a socket. On the other hand, if you try to unregister a key for reading while reading, then you may get errors in threads, because SelectionKeys and their interests can only be handled by a thread that actually calls select (). So overall, this API has some sharp edges, and it's hard to get the correct state handling.
Oh, and another possibility, depending on who closes the socket first, you may or may not notice a closed socket until you explicitly ask. I canโt remember the exact data from my head, but itโs something like this: the client closes its end of the socket, it does not signal an upcoming op on the selection key, so the socket channel is never read.This can leave a bunch of sockets in TIME_WAIT status on the client.
As a final recommendation, if you are performing asynchronous I / O, I definitely recommend several books in the POSA Series of Architecture Oriented Software Products (POSA) series. Volume 2 covers a variety of I / O patterns. (For example, NIO fits very well with the Reactor template in Volume 2. It addresses the many state management issues that I mentioned above.) Volume 4 includes these templates and inserts them into the wider context of distributed systems in general. Both of these books are a very valuable resource.