可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I am having a problem with Java TCP/IP sockets: my Java application will continue endlessly to send data to a server even if the server gets switched off (without a proper TCP/IP disconnect) in the meantime.
I am using the following code to send the data:
PrintWriter out = PrintWriter(socket.getOutputStream(), true);
out.write("text");
if (out.checkError()) {
System.err.println("Error sending string!");
}
In another Stack Overflow question, I found the following answer:
TCP/IP (and therefor java sockets)
will guarantee that you either
successfully send the data OR get an
error (exception in the case of java)
eventually.
Is my code sufficient for getting informed about the TCP/IP stack not being able to successfully send my string or do I need to do something additionally?
Btw: was it correct to open a new question even though the other question was similar? It did not answer my question satisfactorily and I was only able to add a new answer, not a new comment.
回答1:
You might have two problems: PrintWriter
is a strange beast in the Java Stream
/Reader
/Writer
world in that it swallows exceptions and requires you to explicitly check for errors.
The only use for this (in my opinion) are the standard streams (System.out
and System.err
), where failure to write output should not halt the application (for example if no standard output is available).
Replace that with an OutputStreamWriter
and you'll be informed about errors as soon as Java knows about them.
That brings me to the second possible problem: TCP/IP doesn't have any automated keep-alive packets: so if your connection gets severed in some way, you won't actually notice it until you attempt to send data.
So if you connect to some socket, send some packets, wait a bit and then get disconnected, you will only be notified of the fact when you next try to send some data. This is inherent in the TCP/IP protocol and not the fault of Java.
If you want to reduce the problem, then you could send periodic keep-alive/ping messages with no actual effect, except that they check if the connection is still alive.
回答2:
Your question is different enough I'd say. Google has a lot to say about this sort of thing though. The keep-alives (SO_KEEPALIVE
) are not designed for this sort of thing. From what I have read the TCP spec says that they should not be sent more than once every two hours and it is up to your OS to manage it so you don't have much control. I emphasize from what I have read.
Whether you can use them more than once every two hours doesn't matter in your case though since you are continually sending data. Keep-alives are only needed if you want to detect a broken connection while you are not sending data.
If you were using the OutputStream
of the Socket
directly, it would throw an exception when you attempt to send to an unavailable destination (for whatever reason that may be). Since you are using PrintWriter
, you need to check for errors manually using checkError()
.
So, in summary: Yes, it is sufficient for your purpose.
回答3:
What I didn't mention (as I thought it was not relevant at that time) is that I am trying to do this on Android.
After several weeks of testing, though, it seems that Android's implementation of the standard Java networking classes is behaving very differently from the Oracle JRE. On Android, it is apparently impossible to reliably detect if the connection has been closed, even if I closed it myself. [Stream].write()
will try writing for several minutes. So on Android, it appears that you will always need to send your own keep-alives (and check for reception!) for detecting a broken connection.
The other answers to this question will work fine with the Oracle JRE. Thanks again!
If anyone can provide further information on this topic, please do so.
回答4:
Sometimes even though the server has died, your client application is not informed about it. Usually this is a bad router somewhere failing to shut down both sides of the connection. You should set up keep-alives so you can detect this kind of failure.
Depending on your OS there are ways to change the keep-alive test interval.
回答5:
One special condition applies when you are closing the socket. Depending on the socket opt SO_LINGER
(see this article) you set, the close()
call will either return immediately or wait until all pending TCP transmissions were either successful or an error occured, which is then signalled by an IOException thrown from the close()
implementation.
Also SO_TIMEOUT
has an effect on when retries will time out.