Java NIO channel never becomes readable

  • Thread starter nooneinparticular314159
  • Start date
N

nooneinparticular314159

I have a Java NIO channel that is never readable. It does become
writable. I initially register it inside of an event loop as shown
below (I've only included the relevant snippets of code):

SelectionKey NextKey = (SelectionKey) KeyIterator.next();
if (NextKey.isAcceptable()) {
SocketChannel AcceptedChannel = null;
ServerSocketChannel ServerChannel = (ServerSocketChannel)
NextKey.channel();
AcceptedChannel = ServerChannel.accept();
AcceptedChannel.configureBlocking(false);
AcceptedChannel.register(ChannelSelector, SelectionKey.OP_READ |
SelectionKey.OP_WRITE);

and then I test to see if I can read and write:

if (NextKey.isReadable()) {
do fun reading stuff, like reading data from the channel
}

if (NextKey.isWritable()) {
do exciting writing stuff, like writing data to the channel!
}

While I do seem to be able to *write* to the channel, I am completely
unable to read, as the NextKey never becomes readable. I've tried
changing the registration of the channel at various points to read
only or write only, but that doesn't help. What I'm trying to do is
get two clients to talk to each other using NIO. What I have wound up
with is each client yelling at the other, but neither of them
listening to what the other says! What am I doing wrong here? Why
does NextKey never become readable? How can I fix it? I've checked
every Java NIO reference I can find, and nothing helps...

Thanks!
 
M

Mark Space

only or write only, but that doesn't help. What I'm trying to do is
get two clients to talk to each other using NIO. What I have wound up
with is each client yelling at the other, but neither of them
listening to what the other says! What am I doing wrong here? Why
does NextKey never become readable? How can I fix it? I've checked
every Java NIO reference I can find, and nothing helps...

Are you sure you write to the channel? Do you ever flush your writes?

Can you, for example, run telnet listening on the port you are using and
verify it receives what you write?

Can you read from this channel with out using NIO? It might be best to
start with something (test harness first!) easier to debug, then get NIO
working.
 
N

nooneinparticular314159

Are you sure you write to the channel? Do you ever flush your writes?

Can you, for example, run telnet listening on the port you are using and
verify it receives what you write?

Can you read from this channel with out using NIO?  It might be best to
start with something (test harness first!) easier to debug, then get NIO
working.

That's a brilliant idea (telnet)! I'd never thought of trying that.
So I just did, and yes, the data is being written. I can also verify
that it DOES read data by typing a response in telnet. So what seems
to happen is that my program receives the data, but the channel never
gets set to read. So what could be causing this problem, and how do I
get it to read?

Thanks!
 
N

nooneinparticular314159

Are you sure you write to the channel? Do you ever flush your writes?

Can you, for example, run telnet listening on the port you are using and
verify it receives what you write?

Can you read from this channel with out using NIO?  It might be best to
start with something (test harness first!) easier to debug, then get NIO
working.

Transmitting through Telnet, I get the following error, which I
suspect is the result of my sending bogus data through telnet, rather
than following the proper protocol:

Exception in thread "main" java.lang.OutOfMemoryError: Java heap space
at
BitTorrent.NetworkDataHandler.ReadMessageFromChannel(NetworkDataHandler.java:
412)
at BitTorrent.Main.main(Main.java:343)
Java Result: 1

I don't *think* that this is the cause of my not getting readable
channels though, since I've verified that the channel key is never
readable.

The channel does become readable when I transmit through telnet
though, and the client is clearly transmitting data when I connect
with telnet. What is happening here?

Thanks!
 
E

EJP

The channel does become readable when I transmit through telnet
though, and the client is clearly transmitting data when I connect
with telnet. What is happening here?
That indicates that your sending code is incorrect. If it's NIO code, it
needs to flip() the buffer before the write, compact() it afterwards,
and examine the count returned by the write() call. If that was zero,
you need to register the channel for OP_WRITE, and when it happens,
retry all that. If you *didn't* get zero, deregister OP_WRITE.
 
N

nooneinparticular314159

Ok. I think I'm closer, but I'm still doing something slightly
wrong. My writing code looks like:

SetChannelForWritingOnly(); //This method does a
Channel.register(ChannelSelector,
//SelectionKey.OP_WRITE);


SendBuffer.flip();
BytesWrittenToChannel = Channel.write(SendBuffer);
SendBuffer.compact();
SetChannelForReadingOnly();

When I write to the channel, BytesWrittenToChannel is 0. So no bytes
are actually written. So I am registering the channel for writing
before, flipping the buffer before the write, compacting it
afterwards, and still getting 0. Huh?

Thanks!
 
N

nooneinparticular314159

I should probably note that I have two buffers here: a send buffer and
a receive buffer. I always write to the sendbuffer, and I always read
from the receive buffer. I tried getting rid of the flip, and I was
suddenly able to write to the channel (although the other side still
doesn't read it!) The same data seems to get written to the channel
over and over again (possibly until the buffer on the other side fills
up?), after which, I get no ready channels...
 
N

nooneinparticular314159

A further followup - when the data is written to the channel, the
position in the buffer does not appear to change. For example, when I
do:

BytesWrittenToChannel = Channel.write(SendBuffer);

The contents of SendBuffer before and after the write are identical.
The limit, capacity, and position do not change. If I do a
SendBuffer.flip or SendBuffer.clear(), it still doesn't change
anything in the SendBuffer! So the sendbuffer always contains the
data that I originally put into it, it keeps writing out, and it never
gets cleared. That data is received over my telnet connection if I
telnet to the program, but is never read in by my actual client, as
noted previously. Any idea why this might be happening? It's my
understanding that performing a write is supposed to consume the
contents of the buffer (or at least as much as is written), but this
doesn't seem to be happening.

Thanks!
 
N

nooneinparticular314159

Another followup - I've recorded the transmissions using Ethereal (aka
Wireshark). Both clients are clearly transmitting to each other.
(After removing the Channel.flip and clear statements, the clients
transmit properly.) Ethereal shows that the transmissions sent are
exactly what should be sent. The problem is that the clients never
have OP_READ flag set for the channel. So this brings me back to my
original question - why doesn't OP_READ ever get set, and how can I
correct this?

Thanks!
 
E

EJP

The contents of SendBuffer before and after the write are identical.
The limit, capacity, and position do not change. If I do a
SendBuffer.flip or SendBuffer.clear(), it still doesn't change
anything in the SendBuffer!

All that indicates that there is nothing to write, i.e. position = limit.

NB I think the result of a ByteBuffer.wrap() is already flipped for some
reason.

Re OP_READ, are you registering the channel for OP_READ?
 
N

nooneinparticular314159

All that indicates that there is nothing to write, i.e. position = limit.
NB I think the result of a ByteBuffer.wrap() is already flipped for some
reason.

Re OP_READ, are you registering the channel for OP_READ?

I'm initially registering the channel as:
AcceptedChannel.register(ChannelSelector, SelectionKey.OP_READ |
SelectionKey.OP_WRITE);
(I've also tried setting it up intially as:
AcceptedChannel.register(ChannelSelector, SelectionKey.OP_READ);
but that doesn't seem to work (as it shouldn't, since I need to be
able to both read and write.)

The next thing it hits (assuming that there is a key) is usually if
(NextKey.isWritable())
(It hits the test for readability first, but I never get a readable
channel)

It gets the appropriate channel based on that key:
NetworkDataHandler Handler = DataHandlerContainer.Get((SocketChannel)
NextKey.channel());

and then the object associated with that specific channel tries to set
up a message and write to it:
MessageHandlerForThisChannel.SendTestMessage();

That creates a test message (currently a string about wishing that my
program would work!), encodes it in a byte array, and adds it to my
outgoing message queue.

A message sending method is then called, which checks for messages on
the queue. If any exist, it gets the next one and places it in the
SendBuffer. It then does a

Channel.write(SendBuffer);

to write out the data. I've tried various combinations of registering
teh channel for OP_READ, OP_READ | OP_WRITE, or OP_WRITE after this,
and none of them worked (although registering for read only certainly
prevented more data from being written. :) )

After the data is written out to the channel, it definitely appears on
the remote host. Using Ethereal, I've been able to confirm that both
clients send the test message to each other. Using Telnet, I've also
been able to confirm that the message is sent to remote clients.
Ethereal also confirmed that both clients are writing to and from the
same ports on each machine, so they are clearly using the same channel
in each direction. However, despite the data making it at least as
far as the remote host, the OP_READ is never triggered, and therefore
the data is never read. Unless, of course, I send the data through
telnet, in which case it is. Actually, I also tried writing a non-
blocking client that does an OP_CONNECT instead of a read or write,
that one seems to be able to get data through to the client (although
it doesn't try to read any.) That client uses the same reading and
writing methods as my regular client. Only the setup is different.

Any ideas? What else can I try?

Thanks!
 
E

EJP

(I've also tried setting it up intially as:
AcceptedChannel.register(ChannelSelector, SelectionKey.OP_READ);
but that doesn't seem to work (as it shouldn't, since I need to be
able to both read and write.)

Nothing to do with it. You can write any time. You should only register
OP_WRITE when you got a zero return from channel.write(); then when
OP_WRITE fires, repeat the write, and if it succeeded completely (i.e.
!buffer.hasRemaining()), deregister OP_WRITE.
The next thing it hits (assuming that there is a key) is usually if
(NextKey.isWritable())

Of course. The channel is almost always writable, unless you've filled
the send buffer. That's why you should only register OP_WRITE when you
know that has occurred.
(It hits the test for readability first, but I never get a readable
channel)

So there is nothing to read.
However, despite the data making it at least as
far as the remote host, the OP_READ is never triggered, and therefore
the data is never read.

If the remote host is written the same way as this, it is probably too
busy spinning on OP_WRITE. In any case until the remote host has read
the request it won't write a response, will it? which would explain why
you never get a readable channel in the client.
Unless, of course, I send the data through telnet,

Can't account for that, but there are enough mistakes already that you
need to fix and re-test.

in which case it is. Actually, I also tried writing a non-
blocking client that does an OP_CONNECT instead of a read or write,
that one seems to be able to get data through to the client (although
it doesn't try to read any.) That client uses the same reading and
writing methods as my regular client. Only the setup is different.

If you use OP_CONNECT you must deregister it when it fires, as under the
hood it's the same as OP_WRITE.
 
N

nooneinparticular314159

Nothing to do with it. You can write any time. You should only register
OP_WRITE when you got a zero return from channel.write(); then when
OP_WRITE fires, repeat the write, and if it succeeded completely (i.e.
!buffer.hasRemaining()), deregister OP_WRITE.


Of course. The channel is almost always writable, unless you've filled
the send buffer. That's why you should only register OP_WRITE when you
know that has occurred.


So there is nothing to read.


If the remote host is written the same way as this, it is probably too
busy spinning on OP_WRITE. In any case until the remote host has read
the request it won't write a response, will it? which would explain why
you never get a readable channel in the client.


Can't account for that, but there are enough mistakes already that you
need to fix and re-test.

  in which case it is.  Actually, I also tried writing a non-


If you use OP_CONNECT you must deregister it when it fires, as under the
hood it's the same as OP_WRITE.

I'm confused. If you never register OP_WRITE unless the client can't
write to the channel, then how does the code to write in the first
place ever get called? ie. You test to see if the channel is
writable. If it is, you write something. If it isn't, that section
of code will never get called, and you will never get the chance to
write.
So there is nothing to read.

But I'm sure that there is something to read. In fact, I can have one
client write repeatedly until the receive buffer gets filled. Then it
stops writing because it can no longer do so. But even when this
happens, OP_READ is never triggered. Huh?
If the remote host is written the same way as this, it is probably too
busy spinning on OP_WRITE. In any case until the remote host has read
the request it won't write a response, will it? which would explain why
you never get a readable channel in the client.

In this case, both clients are identical. The way it works is one
client connects to the other. when it does so, it sends some initial
data. Both clients send this data first thing, before anything else
happens. So they should both have data to read. But somehow, they
don't.

The OP_CONNECT isn't actually part of my client code - it was just
there from a testing program. But it does trigger a read on the
client...

I'll try setting everything to OP_READ except in the situation you
mentioned and report back what happens. I'm pretty sure that I've
tried this before though, and when I do, the clients just sit there
and don't write (and therefore have nothing to read). I'll also try
removing the test for writing, as I think you've suggested, and maybe
that will help. More shortly...

Thanks!
 
M

Mark Space

I'm confused. If you never register OP_WRITE unless the client can't
write to the channel, then how does the code to write in the first

I'm guessing here, but EJP did say to try to write first. Then, if the
bytes written was 0, only then register OP_WRITE.

It makes sense to me. Only register OP_WRITE if the out-bound channel
is stuffed full and won't take any more, that's when you need to be
notified when it's ready again. Otherwise, just stuff more in there.
 
N

nooneinparticular314159

Results:
Always setting to OP_READ: one client manages to write something to
the channel, but the other never gets a readable key, and therefore
never looks in the buffer.

Always setting to OP_READ + removing the test for NextKey.isWritable:
I get a class casting exception when I try to do:

DataHandlerContainer.Get((SocketChannel) NextKey.channel());

(This has never happened before. The DataHandlerContainer contains
the objects that I use to write to channels. I have one such object
per channel. I have many clients that I might need to communicate
with, which is why I am dependent on the key to tell me which channel
to write to, and therefore which object to have writing to that
channel.)

Always setting to OP_READ, but setting the channel to OP_READ |
OP_WRITE just before I write has no effect. One client still writes.
The other never does. Neither reads. However, occasionally a crash
occurs, and the buffer on the other client suddenly becomes readable
(once with actual data in the buffer!)
 
E

EJP

Always setting to OP_READ: one client manages to write something to
the channel, but the other never gets a readable key, and therefore
never looks in the buffer.

Did the write return a non-zero result? If not, you haven't written
anything yet.
Always setting to OP_READ + removing the test for NextKey.isWritable:
I get a class casting exception when I try to do:

DataHandlerContainer.Get((SocketChannel) NextKey.channel());

That's a bug in your code.
Always setting to OP_READ, but setting the channel to OP_READ |
OP_WRITE just before I write has no effect.

It's not *supposed* to have any effect. These OP_* things just tell the
selector what you want it to wake up on. They're not there to determine
what I/O APIs you're allowed to call.

You'll have to show us some code for this to get any further. You seem
to have a lot of misconceptions about how all this is supposed to work.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,764
Messages
2,569,566
Members
45,041
Latest member
RomeoFarnh

Latest Threads

Top