Java Forum / General / March 2008
Java NIO channel never becomes readable
nooneinparticular314159@yahoo.com - 17 Mar 2008 04:30 GMT 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!
Mark Space - 17 Mar 2008 07:10 GMT > 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.
nooneinparticular314159@yahoo.com - 17 Mar 2008 07:31 GMT > nooneinparticular314...@yahoo.com wrote: > > only or write only, but that doesn't help. What I'm trying to do is [quoted text clipped - 12 lines] > 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!
nooneinparticular314159@yahoo.com - 17 Mar 2008 07:41 GMT > nooneinparticular314...@yahoo.com wrote: > > only or write only, but that doesn't help. What I'm trying to do is [quoted text clipped - 12 lines] > 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!
EJP - 17 Mar 2008 10:34 GMT > 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.
nooneinparticular314159@yahoo.com - 17 Mar 2008 17:05 GMT 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...
nooneinparticular314159@yahoo.com - 17 Mar 2008 18:53 GMT 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!
EJP - 18 Mar 2008 02:03 GMT > 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?
nooneinparticular314159@yahoo.com - 18 Mar 2008 02:32 GMT > 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!
EJP - 19 Mar 2008 00:26 GMT > (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.
nooneinparticular314159@yahoo.com - 19 Mar 2008 01:24 GMT > nooneinparticular314...@yahoo.com wrote: > > (I've also tried setting it up intially as: [quoted text clipped - 42 lines] > 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.
>> (It hits the test for readability first, but I never get a readable >> channel)
>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!
Mark Space - 19 Mar 2008 01:40 GMT > 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.
nooneinparticular314159@yahoo.com - 19 Mar 2008 02:00 GMT 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!)
EJP - 19 Mar 2008 04:29 GMT > 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.
nooneinparticular314159@yahoo.com - 17 Mar 2008 19:19 GMT 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!
nooneinparticular314159@yahoo.com - 17 Mar 2008 16:54 GMT 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!
Free MagazinesGet these publications absolutely FREE for up to 12 months. There are no hidden fees and no obligation. Simply choose a title, complete the application form and submit it. Read more ...
|
|
|