Java NIO: relationship between OP_ACCEPT and OP_READ?

I am rewriting the basic network NIO network code for my project, and I am trying to figure out when I should โ€œstoreโ€ the connection information for future use. For example, as soon as the client connects in the usual way, I save and bind the SocketChannel object for this connected client so that I can write data to this client at any time. I usually use the client's IP address (including port) as the key in the HashMap, which maps to the SocketChannel object. That way, I can easily search by their IP address and send data asynchronously through this SocketChannel.

This may not be the best approach, but it works and the project is too large to change its main network code, although I would consider the suggestions. My main question, however, is this:

At what point should I โ€œstoreโ€ the SocketChannel for future use? I keep the link to SocketChannel after connection (via OP_ACCEPT). I believe this is an efficient approach because I can assume that the map record already exists when the OP_READ event occurs. Otherwise, I will need to do an expensive HashMap check every time OP_READ is encountered, and it is obvious that MANY of them will be executed for the client than OP_ACCEPT. My fear, I think, is that there may be some connections that become accepted (OP_ACCEPT), but never send any data (OP_READ). Perhaps this is possible due to a problem with the firewall or a failed client or network adapter. I think that this can lead to the connection of "zombies" who are not active, but also never receive a closed message.

Part of my reason for re-writing my network code is that in rare cases, I get a client connection that is in a weird state. I think I processed OP_ACCEPT compared to OP_READ, including the information I use to suggest that the connection is "valid" and may be saved, may be incorrect.

I'm sorry, my question is no more specific, I'm just looking for the best, most efficient way to determine if the SocketChannel is really valid, so I can store a link to it. Thanks so much for any help!

+4
source share
2 answers

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.

+4
source

An alternative would be to browse the existing NIO socket infrastructure, possible candidates are:

0
source

All Articles