Java TCP/IP 套接字如何向应用程序报告传输成功或失败?
我在使用 Java TCP/IP 套接字时遇到问题:即使服务器同时关闭(没有正确的 TCP/IP 断开连接),我的 Java 应用程序仍将继续无休止地向服务器发送数据。
我使用以下代码发送数据:
PrintWriter out = PrintWriter(socket.getOutputStream(), true);
out.write("text");
if (out.checkError()) {
System.err.println("Error sending string!");
}
在 另一个Stack Overflow问题,我找到了以下答案:
TCP/IP(以及 Java 套接字) 将保证您 成功发送数据或得到 错误(java的情况下例外) 最终。
我的代码是否足以获悉 TCP/IP 堆栈无法成功发送我的字符串,或者我是否需要额外执行某些操作?
顺便说一句:即使另一个问题类似,提出一个新问题是否正确?它没有令人满意地回答我的问题,我只能添加新答案,而不能添加新评论。
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.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
您可能会遇到两个问题:
PrintWriter
是 JavaStream
/Reader
/Writer
世界中的一头奇怪的野兽。它会吞掉异常并要求您显式检查错误。对此的唯一用途(在我看来)是标准流(
System.out
和System.err
),其中写入输出失败不应停止应用程序(例如例如,如果没有标准输出可用)。将其替换为
OutputStreamWriter
,一旦 Java 发现错误,您就会收到通知。这让我想到了第二个可能的问题:TCP/IP 没有任何自动保持活动的数据包:因此如果您的连接以某种方式被切断,除非您尝试,否则您实际上不会注意到它发送数据。
因此,如果您连接到某个套接字,发送一些数据包,稍等一下然后断开连接,那么只有当您下次尝试发送一些数据时才会通知您这一事实。这是 TCP/IP 协议固有的,而不是 Java 的错误。
如果您想减少问题,那么您可以定期发送 keep-alive/ping 消息,但没有实际效果,只是检查连接是否仍然有效。
You might have two problems:
PrintWriter
is a strange beast in the JavaStream
/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
andSystem.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.
我想说你的问题很不同。不过,谷歌对此类事情有很多话要说。 keep-alive (
SO_KEEPALIVE
) 并不是为此类事情而设计的。根据我读到的 TCP 规范,它们不应每两个小时发送一次以上,并且由您的操作系统来管理,因此您没有太多控制权。我强调来自我读过的内容。不过,在您的情况下,您是否可以每两个小时使用一次以上并不重要,因为您会不断发送数据。仅当您想在不发送数据时检测断开的连接时才需要保持活动状态。
如果您直接使用
Socket
的OutputStream
,当您尝试发送到不可用的目的地(无论出于何种原因)时,它会抛出异常。由于您使用的是PrintWriter
,因此需要使用checkError()
手动检查错误。所以,总而言之:是的,这足以满足您的目的。
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 theSocket
directly, it would throw an exception when you attempt to send to an unavailable destination (for whatever reason that may be). Since you are usingPrintWriter
, you need to check for errors manually usingcheckError()
.So, in summary: Yes, it is sufficient for your purpose.
我没有提到(因为我认为这在当时不相关)是我正在尝试在 Android 上执行此操作。
然而,经过几周的测试后,标准 Java 网络类的 Android 实现的行为似乎与 Oracle JRE 非常不同。在Android上,显然不可能可靠地检测连接是否已关闭,即使我自己关闭了它。
[Stream].write()
将尝试写入几分钟。因此,在 Android 上,您似乎始终需要发送自己的 keep-alive(并检查接收情况!)以检测断开的连接。这个问题的其他答案可以很好地与 Oracle JRE 配合使用。再次感谢!
如果有人可以提供有关此主题的更多信息,请这样做。
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.
有时,即使服务器已经挂掉,您的客户端应用程序也不会收到通知。通常,这是某个地方的坏路由器无法关闭连接的两端。您应该设置 keep-alives这样你就可以检测到这种故障。
根据您的操作系统,有多种方法可以更改保持活动测试间隔。
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.
当您关闭套接字时,适用一种特殊条件。取决于套接字选项
SO_LINGER
(请参阅此article),close()
调用将立即返回或等待,直到所有挂起的 TCP 传输成功或发生错误,然后由 IOException 发出信号抛出从close()
实现。SO_TIMEOUT
还会影响重试超时的时间。One special condition applies when you are closing the socket. Depending on the socket opt
SO_LINGER
(see this article) you set, theclose()
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 theclose()
implementation.Also
SO_TIMEOUT
has an effect on when retries will time out.