0

I'm trying to write a socket server in java using the java nio classes. Only using non-blocking io, not async io.

I've got a single thread which calls the selector's select() method. Initially the only channel in the select list is a ServerSocketChannel. Each time select() returns I enumerate the selectedKeys() list and delegate the io to a separate io thread. Before I send the information to the io thread I first attempt to turn off the interest on the channel by setting the interestOps to zero. I do this so that the select() isn't firing anymore on that channel since it should continue to fire until the io has been performed. By the way, you can't accomplish this via the SelectionKey's cancel() method as that puts the channel on the cancelled list and it can't be added back to the selection list. cancel() is only for removing the channel from the list when you're done with it. I don't use cancel() at all since closing the socket() will automatically call cancel().

When the io thread finishes the io it communicates back to the select thread asking it to either close the socket or reset the interestOps to what they should be.

While this seems to be somewhat working, I've noticed that select() seems to continually return socket channels even when there are no bytes to read. By the way, the server is only reading from the socket and only sets the interest to OP_READ (or zero when I want to turn off the channel). To test a specific scenario I wrote a client which opens a socket, writes 1k bytes to it, sleeps 10 seconds, and then writes another 1k bytes to it then closes the socket. During this sleep time the select() call returns continuously and when I read the socket there are zero bytes returned. Obviously I don't want the select() loop firing continuously when there is no work to do as I would be pegging a CPU. I've set the channel up for non-blocking io. I should also mention that, similar to one of the threads I reference below, this behavior doesn't happen until the client opens the socket and starts sending data. In this case I still have the server socket channel I'm listening on for OP_ACCEPT and the select() call is not constantly firing for that.

Here are two similar, but different, threads:

Infinite loop with Selector even there's no connection from client

Java Selector returns SelectionKey with OP_READ without data in infinity loop after writing to channel

Some of the comments in those two threads talk about handling the EOF, -1 on read(), case. This is not the issue I'm having so please don't mark this as a duplicate of those.

Also, in one of those threads it sounded like someone suggested that since the socket channel is in non-blocking mode the select() will fire continuously regardless of whether there is, in my case data to read. This sounds very odd. I would find it hard to believe this is the way it's supposed to be working. I believe when I indicate I have an interest in read on a channel, select() should only return that channel in the selected list if there is data to read.

Thanks, Nick

Community
  • 1
  • 1
  • Try to do everything on the selector thread. Otherwise, it gets tricky how keys, keysets, interest updates, etc. work in concurrent threads. For example, how does your io thread actually communicate to the selector thread to reset interestOps? – ZhongYu Oct 12 '15 at 22:56
  • I use two pipes to communicate between the io thread and the selector thread. One pipe for adding channels back into the list and one for closing channels. Each pipe is also assoicated with a queue. Each byte in the pipe corresponds to an item in the associated queue. These pipes are in the selector list. I did this so that if we need to add additional threads for handling io it might simplify things as opposed to having multiple selector threads. –  Oct 13 '15 at 13:35

1 Answers1

0

It sounds like you aren't removing the selected key from the selected-key set while iterating. There are several examples of doing it correctly in the threads you linked.

NB one of them states that select() will loop indefinitely if there are no events. That will only happen if there is a positive timeout value: it will timeout and return zero, repeatedly. If there is no timeout it will block indefinitely.

NB cancel() doesn't actually prevent you from adding the channel back to the selection list. You would have to re-register the channel, and there might be an issue if select() hasn't executed between the cancel and the re-registration (as it may not have processed its internal cancel list). But there's nothing wrong with the way you're doing it now.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • You can register the channel after you have called cancel on the selection key. If you attempt to you'll get a CanceledKeyException. –  Oct 12 '15 at 22:53
  • OK that's because `select()` hasn't run in between, as suggested. You could avert that by calling `selectNow()`, but it's not great technique, and not necessary. – user207421 Oct 12 '15 at 22:54
  • That is true, I'm not removing the items from the selected key list. I'm just iterating through the list and then calling select() again. I assumed that select() returned me a new selected key list each time. I will certainly try removing each selection key from the list and see if that fixes my problem. –  Oct 12 '15 at 22:56
  • 2
    No. It never clears the selected-key set. You must call `Iterator.remove()` after `Iterator.next()`, or clear the set yourself at the end of the key loop. – user207421 Oct 12 '15 at 22:56
  • Just want to correct one statement I made above. For some reason I can't edit it anymore. My first comment above should read: 'You *can't* register the channel after you have called cancel on the selection key. ...' –  Oct 12 '15 at 23:06