non NIO question

Hi
Having written network code several times, including a few NIO attempts, I thought I would look at how others have done it :). I’m reading blahblahblah’s docs on NIO and came across something that has got me thinking (doesn’t happen very often mind). In the section where he discusses the old way of networking, he comments on the fact that if you have a latency of 250ms, then to iterate over 4 players and send them the same message (assuming the same latency) that it will take 1000ms. I was under the impression that the old IO dumped the bytes down to the tcp stack and let it deal with things having returned the thread of execution back to the caller. The impression I get from the doc is that you have to wait for acks to all packets before it returns, am I reading it wrong, is it written wrong, or is this really the case???, it might explain a few of the wierdeties i’ve seen :slight_smile:

Cheers

Endolf

[quote]Hi
Having written network code several times, including a few NIO attempts, I thought I would look at how others have done it :). I’m reading blahblahblah’s docs on NIO and came across something that has got me thinking (doesn’t happen very often mind). In the section where he discusses the old way of networking, he comments on the fact that if you have a latency of 250ms, then to iterate over 4 players and send them the same message (assuming the same latency) that it will take 1000ms. I was under the impression that the old IO dumped the bytes down to the tcp stack and let it deal with things having returned the thread of execution back to the caller. The impression I get from the doc is that you have to wait for acks to all packets before it returns, am I reading it wrong, is it written wrong, or is this really the case???, it might explain a few of the wierdeties i’ve seen :slight_smile:
[/quote]
Good question. However, you are almost perfectly describing an “asynchronous I/O system”. Perhaps this makes it clearer why so many people hate synch I/O? :wink:

A true synchronous I/O system ought to block by definition until “all the data has been confirmed received”; this would imply you have to wait the full latency.

Non-java applications that use synch TCP communications do exactly that, as you will know if you have a 56k (or slower) modem - especially when the re-dial starts! I only mention this because this is an example most people have real experience of :slight_smile: - although there is potential for confusion between this and “waiting for DNS timeouts” which has identical symptoms.

Anyway, think about exceptions: if control passes back to the program before the entire TCP send has completed, been acked, etc, then how the heck do you throw an IOException when someone unplugs the network cable during transfer of the very last byte? :). One of the reasons synch I/O is easier for people like Sun to implement for us to use is that they don’t have to do any of the “delayed notification” etc implicit in asynch I/O: all possible outcomes are contained within one method call.

In summary, this is why synchronous I/O mandates thread-per-client in most situations.

[quote]Anyway, think about exceptions: if control passes back to the program before the entire TCP send has completed, been acked, etc, then how the heck do you throw an IOException when someone unplugs the network cable during transfer of the very last byte? .
[/quote]
Duh, should have spotted that one a mile off, at this particular moment in time I feel like I shouldn’t be a software engineer for a living, and maybe refocus to refuse management or private transportation consultant (bin man/taxi driver).

Cheers

Endolf

Erm. i dont think this is correct. I believe it returns as soon as the OS call finishes and that the OS call queues packets for output.

But I’ll check with our net guys for sure.

Looks like you have something to fix, Blah.

From the networking engineer who does the libraries…

"Do you mean DatagramSocket.send()? or do you mean Socket.getOutputStream().write()?

Either way, the data is buffered in the OS and the call is returned as soon as the data is written to the buffer."

To answer your hypothetical, you DON’T necessarily get an exception for any given packet, you get an exception when the OS has registered a failure. That may not be registered until multiple packets have been queued up.

[quote]Look like you have something to fix, Blah.

From the networking engineer who does the libraries…

"Do you mean DatagramSocket.send()? or do you mean Socket.getOutputStream().write()?

Either way, the data is buffered in the OS and the call is returned as soon as the data is written to the buffer."

To answer your hypothetical, you DON’T necessarily get an exception for any given packet, you get an exception when the OS has registered a failure. That may not be registered until multiple packets have been queued up.
[/quote]
Cool, thanks. When endolf originally asked, I had to go away and think about it and try and work out what was actually happening. I had been taught a long time ago precisely what this engineer has stated, but spurred on by endolf’s question to really think about this I’m now a little disturbed.

(N.B. in the article, I was attempting to illustrate some of the theoretical differences between IO and NIO; because of variance from JVM to JVM anything that’s not mandated by Sun e.g. within the API docs is not guaranteed to hold anywhere, though it may be true somewhere. I’ve recently been revising the article to add more examples of such theoretical differences, and also to make more clear that I’m only highlighting how your VM’s may be expected to work; you can never be sure which JVM’s will observe which differences. I’ll be glad to also make clear the current state of Sun’s VM’s on this issue…)

Anyway… semantically within java, can you get an exception except inside the method call to trigger the send (I suggest let’s just concentrate on the write() method of the output stream for now, since it’s the most commonly used…)?

I’m sure I’ve got the wrong end of the stick here, but…The response you’ve been given makes it sound like there are exceptions that will only be thrown if you make a SUBSEQUENT call to the same method - simply because they were waiting in limbo for the opportunity to happen (i.e. even if the OS signalled java, java has moved on and it’s now illegal for java to throw the exception)?

Another thing that has me confused now is the observed behaviour that java can (and does!) hang indefinitely on attempts to synchronously send a few hundred bytes of data until the receiver actually starts reading it from the receiver’s local buffers. Just guessing here, perhaps this observation is due to java peeking at the TCP state, observing that although it’s dumped the data into the send buffer, the remote host is not reading it, and blocking anyway?

The most recent examples I’ve seen of this are simple HTTP communication - if you set up a simple IO-based HTTP Server, listen to a serversocket, accept the incoming connection, but don’t make the read() call on the socket, then the sender (in a different class, connecting to the serversocket, and calling write()) blocks indefinitely. I haven’t done this for quite a long time (I use NIO exclusively these days :)), so if this differs from other people’s experience I’ll have to try and dig out old code and see what I’m misremembering. Assuming my memory’s correct, it sounds like my interpretation of this symptom has been wrong all along? :slight_smile:

Well this is a bit of a guess on your HTTP behavior. Id have to dig into a test case to have a real intelligent opion BUT…

My guess if this effect is real is that there is some part of the connection handshake that the OS isn’t completing until the first read. Nasty but quite possible. Have you tested to see hat hhappens if you do the initial read and then block on subsequent reads on the same connection? My guess would be that it would allow you to pump data unti lthe buffer was filled then block.

The OTHER possability I’d think is that your system has very tiny networking buffer and that you or some other code running at the same time as you has managed to fil it up. It certainly IS true that if yo uare sending data at a faster rate then you can read it, eventually you WILL indeed back up and block.

Oh and on the exceptions.

While it IS possible for Java to throw an exception outside of a declared exception chain (using children of RuntimeException, one common example being a null pointer exception), in this case I don’t thin kanythign that complex is needed.

You are always going to call SOMETHING after a write, either another write or a close(). I think its likely that close() DOES block until the last byte is acked, but again thats really up to the OS. We just report what it tells us :slight_smile:

Hmmm
Thanks for replying to this one jeff. It sounds like things work pretty much the way I had assumed before I read the article. Nice to know I’m not quite as mad as I thought I was :slight_smile:

Endolf

Doh. Of course, close() is the catch-all here.

However, surely that sucks for application development, because you may well send data and then do nothing with that client for minutes (yes, I know a lot of apps have to resort to heartbeats anyway to get around bugs in the IO design of Java, so perhaps it just gets absorbed by that). FPS’s don’t care, but there are quite a lot of games where a net connection may go silent for a number of seconds.

[quote]Hmmm
Thanks for replying to this one jeff. It sounds like things work pretty much the way I had assumed before I read the article. Nice to know I’m not quite as mad as I thought I was :slight_smile:

Endolf
[/quote]
Sorry about that; I should obviously open my big mouth more often before thinking :). (my initial reaction was “oops; that’s only intended as a theoretical difference to contrast synch with asynch” before I started thinking about it).

[quote]However, surely that sucks for application development, because you may well send data and then do nothing with that client for minutes (yes, I know a lot of apps have to resort to heartbeats anyway to get around bugs in the IO design of Java, so perhaps it just gets absorbed by that). FPS’s don’t care, but there are quite a lot of games where a net connection may go silent for a number of seconds.
[/quote]
Another nasty of this is that if you do a send and get an exception you assume that it was that message that broke it, when in fact it might well be that the exception is left over from your previous write, and that it was the previous message that failed, and your sat there thinking it got the last message but not this one, especially if you are trying to then re-establish the connection and state to recover from the error means you have to rewind an unknown amount :slight_smile:

Endolf

[quote]Sorry about that; I should obviously open my big mouth more often before thinking :). (my initial reaction was “oops; that’s only intended as a theoretical difference to contrast synch with asynch” before I started thinking about it).
[/quote]
Thats the problem of being known to be right so often, people read everything you write literally :). If your wrong as often as I am people take everything you write with a pinch of salt so it doesn’t matter if it’s not 100% accurate :slight_smile:

Still a nice article, although full source at the end for the NIO bits might be usefull to NIO newbies (like how to create a selector in the first place)

Cheers

Endolf

LOL. I just wish I knew how that felt :)…normally I get accused of always thinking I’m right, and people feel less inclined to believe me because they’re sure I must be wrong sooner or later ;). Ah, the joys of english cynicism! :slight_smile:

Thanks. Just as long as it helps people…

Ah. Good point. I hate the modern tendency to write HTML articles that are mostly “simple vague overview; description of source code; downloadable source code”. That’s not a tutorial, it’s an open-source project without the benefits of being able to update it ;).

So I accidentally veer too much away from any source code, unless it’s short, concise, and can be fully understood with just one read through. But…I’d been wanting to provide a framework for getting started, and with a NIO server that’s a lot of code. A few complete class files at the end will probably be perfect for this. Thanks :).

I thought Jeff said he was going to ask an appropriate networking person in sun about this…but I can’t seem to find the post. Hmm. Was I thinking of a different thread? ???

Look back about 4 messages. I already did and you already responded to it.

Or was there another thing I was suppsoed to chase?

[quote]Look back about 4 messages. I already did and you already responded to it.

Or was there another thing I was suppsoed to chase?
[/quote]
:slight_smile: No, not that. I’d thought you were going to ask about the issues we came up with based upon the first response you obtained :).

Probably. Could you remind me what those were?

[quote]Probably. Could you remind me what those were?
[/quote]
Sure (all in response to your post on the previous page, about what you learnt from the Sun / VM guy…)

  1. What about… for a particular client, you may well send data and then do nothing with that client for minutes. FPS’s don’t care, but there are quite a lot of games where a net connection may go silent for a number of seconds. Are you saying the Exception for the “last byte” (worst-case scenario) doesn’t get thrown until your next write, which could be seconds, or even minutes, later? Then your game (clients or servers) could take huge amounts of time (e.g. seconds) to notice a synchronization problem, and perhaps get kicked out of the game (the other side notices the problem much sooner, and timesout waiting for a reconnect)…

EDIT: Or, are you saying that certain IO errors do NOT throw exceptions in Java - e.g. the termination of a TCP connection during the actual send from the NIC buffer? (this is the only obvious alternative explanation I can think of…)

  1. (more complex than 1)

In particular, doesn’t the described design (for blocking IO) break a theoretically valid server design?:

a. Send data to client C1; if there’s an exception, send data to backup-client C2
b. (several seconds later) Send different data to C1; if there’s an exception, send data to backup-client C3.

But…with the described blocking IO impl, if there were a problem with the OS sending the data to C1 in a), you won’t (MIGHT NOT - i.e. depends how “early” the problem occurs) find out until b), and then will resend the WRONG data to the WRONG backup client.

(note: this is NOT obviously connected to game servers, although I think I can think of examples that are ;), but this is the simplest version of this problem I could think of, for the sake of clarity :)…)

In conclusion, a “blocking I/O call” to me suggests it blocks until the I/O is performed! Writing to the OS buffer and then returning to the app is not blocking-IO (to me), it’s “blocking method calls” - i.e. only the basic data transfer to another part of your local machine has been completed, there’s been no “block” to wait for the actual I/O to complete!

However, I’m pretty sure I’m making some basic mistake of misunderstanding something here; so I’m probably assuming something I shouldn’t be, or vice versa. Hopefully the example above illustrates this sufficiently. (PS I think I’ve included Endolf’s queries too, if not I’m misunderstanding you as well :)!)

Hi
Not misunderstood me, point 2 was pretty much what I was thinking of :), only worded better :slight_smile:

Cheers

Endolf