6

Currently I'm working on a Java NIO Server (single-threaded) and have encountered some problems. The server accepts incoming connections, writes initial packets (the packet contains some data that client uses for further communication) to clients but doesn't read from them. The server tries to read only when I close the client and, of course, it returns -1.

When accepting connection, it's being registered under:

selectionKey = socketChannel.register(_selector, SelectionKey.OP_READ)

selectionKey.isReadable() returns false (should it?)

Before sending initial packet, ops are changed to:

_selectionKey.interestOps(_selectionKey.interestOps() | SelectionKey.OP_WRITE)

After sending initial packet, ops are changed to:

selectedKey.interestOps(selectedKey.interestOps() & ~SelectionKey.OP_WRITE)

Packet gets sent.

What could be the problem? Can it be related to client?

Mike Samuel
  • 118,113
  • 30
  • 216
  • 245
Testas
  • 63
  • 1
  • 4
  • 1
    Unless this is completely necessary, you should not build your own NIO server, this is not simple and there are a lot of caveats, please use a library that abstracts the complexity for you like Netty ( http://www.jboss.org/netty ) or Mina ( http://mina.apache.org/ ). – Maurício Linhares Aug 14 '11 at 03:24
  • @MaurícioLinhares From what I've seen of Netty and MINA they are just as hard to drive as NIO. – user207421 Feb 01 '15 at 00:37
  • Wow, this is actually an ancient question, but they're *very* different from NIO. The New NIO API actually brings some of the abstractions from Netty/Mina into the JDK but they are still very far away from what you get by using something like Netty. – Maurício Linhares Feb 01 '15 at 06:27
  • I have to say, I looked at Netty as an option for my servers (I still plan on it in the future), but I found that without already having a good understanding of NIO in the first place Netty seemed just as complicated. I really like the project though. – mal Jun 30 '15 at 07:32

2 Answers2

6

selectionKey.isReadable() returns false (should it?)

Certainly, until there is data to read, or end of stream.

Before sending initial packet, ops are changed to:

_selectionKey.interestOps(_selectionKey.interestOps() | SelectionKey.OP_WRITE)

Bad idea. OP_WRITE is almost always ready, i.e. except when the socket send buffer is full, so you will just cause your Selector.select() method to spin mindlessly.

When you want to write to the channel, just write. Do that in the classic loop:

while (buffer.position() > 0)
{
    buffer.flip();
    int count = channel.write(buffer);
    buffer.compact();
    if (count == 0)
    {
        // see below ...
    }
}

If count is zero, you should then register for OP_WRITE, break out of the loop, and go back to the Selector loop. If you got out of this loop without that happening, deregister OP_WRITE.

Note that this implies you have a write buffer per channel. For similar reasons (read() returning zero) you also need a read buffer per channel. That in turn implies a channel 'session' object that contains them both, and that is probably the attachment of the channel's selection key.

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Selector.select() wasn't spinning mindlessly as the packet got sent and buffer.remaining() returned 0. Anyways. Result is the same: accepts, writes and that's it. What I meant by "doesn't read" wasn't that read() returns zero. Channel is not even being tried to read from. Selector.select() sees OP_WRITE as ready (when I change interestOps() to | OP_WRITE) when switching readyOps() but never OP_READ (even if interestOps() are changed to ONLY OP_READ). That's the problem. – Testas Aug 14 '11 at 11:35
  • (Couldn't edit the previous comment) Selector.select() sees OP_READ only when I close the client. – Testas Aug 14 '11 at 11:55
  • Okay. Just tried a different client and reading works fine. By the way, client is a MMORPG. So, I guess, that problem was related to the other client or... I was handling it in the wrong way (?). – Testas Aug 14 '11 at 14:34
  • @Testas (1) There is still no point in registering OP_WRITE other than in the situation I described. (2) If OP_READ didn't trigger, no data arrived. It's as simple as that. – user207421 Aug 15 '11 at 00:20
0

OP_WRITE is only needed in the rare circumstances where you want to control lagging, in other words, when the receiver is too slow or the sender is too fast.

Richard Bradley
  • 241
  • 2
  • 8
  • 1
    "when the receiver is too slow or the sender is too fast" are the same condition, not two separate conditions. – user207421 Jun 22 '15 at 03:13