The SelectionKey.attach() method - java

Please give me the concept of the atach() method. Could I work with NIO without atach() method?.
I have read that with this method we can attach any object in a determined SelectionKey, but, I don't find a motive for this, because after registering my SocketChannel within Selector with the READ and WRITE operations, I could read or write toward or from some socket without attaching a buffer, when the Selector tell me that there are read keys, to write I could create a bytebuffer and fill it and write to the socket, and to read I could create a bytebuffer too.
Then, why the programers use atach() method when they register their channels with READ or WRITE operations?
SocketChannel cliente=SocketChannel.open(localhost);
cliente.configureBlocking(false);
Selector selector=Selector.open();
SelectionKey key=cliente.register(selector, SelectionKey.OP_READ | SelectionKey.OP_WRITE);
//the programers ByteBuffer buf=ByteBuffer.allocate(100); key.attach(buf);
...............................
...............................
selector.select();
Set<SelectionKey> readyKeys=selector.selectedKeys();
Iterator<SelectionKey> iterator =readyKeys.iterator();
while(iterator.hasNext()){
SelectionKey key2=iterator.next();
iterator.remove();
if(key2.isReadable()){
SocketChannel cliente2=(SocketChannel)key2.channel();
ByteBuffer buf=ByteBuffer.allocate(100) //The programers: ByteBuffer buf2=(ByteBuffer)
//key2.attachment();
cliente.read(buf);
}
if(key2.isWritable()){
SocketChannel cliente2=(SocketChannel)key2.channel();
ByteBuffer buf=ByteBuffer.allocate(100) //The programers: ByteBuffer buf2=(ByteBuffer)
//key2.attachment();
buf2.put("Hello Server".getBytes("UTF-8"));
cliente2.write(buf2);
}
}

please only give me the concept of the atach() method
There is no atach() method. There is however an attach() method: "Attaches the given object to this key. An attached object may later be retrieved via the attachment method. Only one object may be attached at a time; invoking this method causes any previous attachment to be discarded. The current attachment may be discarded by attaching null."
I want to know only that please. Could I work with NIO without atach() method?
Yes, assuming you mean the attach() method.
Indeed thank you for answer my question. I have read that with this method we can attach any object in a determined SelectionKey, but, I don't find a motive for this, because after registering my SocketChannel within Selector with the READ and WRITE operations, I could read or write toward or from some socket without attaching a buffer, when the Selector tell me that there are read keys, to write I could create a bytebuffer and fill it and write to the socket, and to read I could create a bytebuffer too.
You could indeed, and you would be creating a lot of buffers, one per read, when you should be creating one per channel and associating it via the SelectionKey.attach() method.
Then, why the programers use atach() method when they register their channels with READ or WRITE operations?.
To associate some context with the channel. The attachment can be just a buffer, or it can be some kind of context object that contains a buffer, or two, and maybe a session identifier, user credentials, all sorts of things.

Related

Different ways how to switch channel between read and write modes - when to use which?

Reading various manuals on programming Java NIO socket two different was are used to switch channel/selector mode (expected next operation) between SelectionKey.OP_READ and SelectionKey.OP_WRITE, in order to read or write into the channel. One way is using SelectionKey.interestOps() call, the other to use socketChannel.register().
SelectionKey key = (SelectionKey)keyIterator.next();
...
SocketChannel socketChannel = (SocketChannel) key.channel();
...
socketChannel.register(selector, SelectionKey.OP_READ);
// or ?
key.interestOps(SelectionKey.OP_READ);
The SelectionKey and SocketChannel are clearly bound together, at least in one key loop iteration. Selector is linked to both indirectly. Just saying.
There code source for sun.nio.ch.SelectionKeyImpl.interestOps() is NOT provided with JDK. The source code for java.nio.channels.spi.AbstractSelectableChannel.register() is provided, and yes, internally it indeed calls SelectionKey.interestOps() but also SelectionKey.attach(), the key is there is obtained by a lookup which I see unnecessary as we already have it; and couple of checks. So, both calls do same job under the hood, just that register does a bit more.
My question is: which one to use and when?
2019-09-04 update:
According to the The Rox Java NIO Tutorial, frequently cited on SO, thou quite dated, it's from times before Java 5; what they do is what was suggestested in the comments here:
socketChannel.register(this.selector, SelectionKey.OP_READ) is done only once, on initial accept().
subsequently they use only
key.interestOps(SelectionKey.OP_WRITE) and key.interestOps(SelectionKey.OP_READ)
I also posted a related question: Is it necessary to register interest to write to a NIO socket to send data? investigating when, and if, it is actually necessary to register interest for OP_WRITE.

Is it necessary to register interest to write to a NIO socket to send data?

Is it necessary to register interest to write to a NIO client socket channel to send data? Do I have to always call socketChannel.register(selector, SelectionKey.OP_WRITE), or some equivalent, before writing to the client SocketChannel to be able to write there?
Would not be enough simply to write data to client SocketChannel with channel.write(outputBuffer) and awake potentially blocked Selector, all in a client thread? The main selector loop would then look like this:
Selector selector = SelectorProvider.provider().openSelector();
serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
while (selector.select() > 0) {
Set<SelectionKey> readyKeys = selector.selectedKeys();
Iterator<SelectionKey> keyIterator = readyKeys.iterator();
while (keyIterator.hasNext()) {
SelectionKey key = (SelectionKey)keyIterator.next();
keyIterator.remove();
while (keyIterator.hasNext()) {
...
if (key.isAcceptable()) {
...
SocketChannel socketChannel = serverSocketChannel.accept();
socketChannel.configureBlocking(false);
socketChannel.register(selector, SelectionKey.OP_READ);
// client socket channel would be permanently in the read mode
...
} else if (key.isReadable()) {
...
} else if (key.isWritable()) {
// the key should know here that the underlying channel
// has something to be send to the wire, so it should get
// here if there are still data to be sent
socketChannel.write(outputBuffer)
}
It would only get to the if (key.isWritable()) branch when there is something remaining to be send, the remainder of data from the initial channel.write(outputBuffer) call; like when the message is too long and needs to be send out into chunks and I want no blocking. The loop would spin until outputBuffer.hasRemaining() is finally false.
I am even thinking, does have to be writing to Channel, that is sending data out, done through Selector at all? Leave only incoming traffic to be handled with Selector, as only incoming traffic need wait states?
UPDATE
From further reading of valuable user207421 posts, which I partly inspired me to post this question, NIO Javadoc, in the push to join the dots, I summarized this:
SocketChannel and SelectionKey are thread safe.
Reading and writing from/to channel socket outside the selector loop is indeed possible; it's independent of it. Reading outside does not make much sense, writing to socket definitively does and it's in fact how most of writing is done.
Writing of data is typically initiated outside the selector loop, in a different thread. write(buffer) will not necessary send all data - if the message is too huge for the socket buffer, remote client is very slow or even went unresponsive. In that case write() has to be called repeatedly. Buffer updates the current position with every call, depending how much was sent, which can be anything between 0 and buffer.remaining().
If write() returns 0 then, and user207421 cannot stress this more, it's the case, possibly the only one, when OP_WRITE needs to be registered and writing is then handled within selector loop.
When all data are written the OP_READ must be immediately restored
Having OP_WRITE set without any data to write would cause selector loop to endlessly spin; waste of CPU time.
It is unclear what to do if write() returns >0 but still not all remaining message data were sent, that is buffer.remaining() > 0. Should I also set OP_WRITE and pass it to selector loop, or loop write(buffer) inside writing thread until there is no remainder or 0 is returned?
Can actually such situation happen? Official SocketChannel Javadoc says initially Unless otherwise specified, a write operation will return only after writing all of the r requested bytes ..meaning, no, it cannot happen! But then they add: Some types of channels, depending upon their state, may write only some of the bytes ..so does it mean write() can return before sending all? Or does that mean that returns '0' if it sent, say, just half of the buffer?
In the write returned 0 scenario, when writing is passed to the selector loop, ie different thread; is the writing thread blocked to write a completely new message, ie new buffer, to the same socket channel? Or I have to ensure it does not happen by other means? The Javadoc says: This method may be invoked at any time. If another thread has already initiated a write operation upon this channel, however, then an invocation of this method will block until the first operation is complete. Does that cover my scenario?
Official Javadoc does not mention any special meaning for 0 as a return value for write() method. It just says: The number of bytes written, possibly zero. Hmm.
I'm also not sure how unresponsive or slow client is handled during the write. I'm guessing that returning 0 from write() and passing it to selector loop is exactly the reason it's done like that. Selector then can, and I'm only guessing here, manage writing in an optimal way: speeding it, slowing it, increasing intervals, sending smaller chunks, whatever; whatever the underlying TCP/IP stack reports back.

Does the order of event registration in NIO matter?

The canonical way of using a Selector in java NIO is :
regist the a ServerSocketChannel with the SelectionKey.OP_ACCEPT event type .
invoke select method (which block the thread)in a loop method
then when a OP_ACCEPT event happened , and the relative AcceptEventHandler is invoked to accept a SocketChannel
then regist the accepted SocketChannel with the SelectionKey.OP_READ event type.
when a read event happend , the ReadEventHandler handle the inputs and then regist the SocketChannel with a SelectionKey.OP_WRITE event type.
My question is, why don't register three event type at once at the beginning? Does the sequence make any sense?
The canonical way of using a Selector in java NIO is:
No it isn't. See below.
why don't register three event type at once at the beginning?
Because you can't. You don't have the accepted channel until you call accept(), and you don't do that until you have registered OP_ACCEPT and had it fire, and you can't register the accepted channel for anything until you have it.
does the sequence make any sense?
Nothing else would make sense.
NB you don't register OP_WRITE until you've encountered a short or zero-length write. The reason is that it is almost always ready, so the strategy is simply to write when you have something to write, and only use OP_WRITE to tell you when it becomes possible to write again after a short write (which means the socket send buffer was full).
I'm going to try to add something to the conversation.
A ServerSocketChannel can only accept() new connections. OP_READ / OP_WRITE won't do you anything there. I'm pretty sure you can add them but they will just be ignored because the ServerSocketChannel's only responsibility is to accept() remote SocketChannel.
Once you accept() and get a new SocketChannel; you want to listen to OP_READ first. If you listen to OP_WRITE then most likely you will get the OP_WRITE returned to you every single time you call select() and that will consume lots of resources.
You only want to listen to OP_WRITE when you tried to write some data to the SocketChannel and not all the data was written.

How can i check if there is incoming data from a TCP Socket without receiving it?

Basically what i need to do is to get a notification, then proceed to receive the data using the class DataInputStream and the method read().
The problem is that the DataInputStream does not have a method to check if there is something to read and by doing tests with read() method would interfere with further calls to read().
I can do a hack to make the test by reading one byte and the append further data to it, but i would like to see if there is a more elegant solution.
DataInputStream does not have a method to check if there is something to read
Yes it does. You missed the available() method, but it doesn't always return non-zero, depending on what you're connected to.
Have a look at PushbackInputStream.

Obtaining specific SocketChannel for OP_WRITE modification and write operation

I am implementing a simple chat server using Java NIO for performance reasons and I have read from several sources where they advised against registering a SocketChannel with a Selector to listen to both read and write events at the same time, but only register for write event when there is data available to be written.
Now what I don't know is how to obtain a specific SocketChannel object for OP_WRITE modification and write operation.
Here is the scenario, 20 chat clients establishes a connection with the chat server at a given time. ChatClient 1 sends a message that is meant to be delivered to ChatClient 15. After reading the data from ChatClient 1's SocketChannel, how do I obtain ChatClient 15's SocketChannel specifically which I intend to modify to OP_WRITE and then write the data meant for it.
I don't know if this approach will work, but I was thinking of adding the SocketChannel object returned when ServerSocketChannel.accept() method is called to a list with an index that can be used to look it up later. So when there is data available to be written to Client 15, I look up the SocketChannel object from the list and then perform the OP_WRITE modification and then write the data. Will this work?
Thanks for your time.
It's just a data structure problem. You know which client you want to write to, so you just need a data structure you can get his SocketChannel from. You can key it any way you like: a sequential index, a client identifying string, his IP address; whatever works for your application.

Categories