我已经用Java创建了一个TCP Web套接字客户端,并将其连接到远程TCP服务器。在正常情况下,它可以正常工作。
我需要检测套接字断开并在断开触发后处理一些逻辑。我打算通过在套接字客户端写入其输出流以发送消息时捕获异常来检测断开连接。
但是,当我关闭wifi连接或拔掉套接字客户端的LAN电缆时,它在几秒钟内无法识别与服务器的断开连接。客户端识别与服务器的断开连接并在读取时将异常抛出以下状态需要花费不同的时间段(秒或分钟)。
java.net.SocketTimeoutException:读取超时。
我担心的是在套接字客户端抛出上述异常之前的断开连接期间,它成功地将消息写入其输出流,并且没有引发任何套接字异常。因此,我的某些消息迷路了。
下面是我的示例套接字客户端的实现,用于测试场景(Java)。
创建套接字连接
public void connect(String ipAddress, int port) throws MxLightAgentException {
try {
if (socket != null) {
socket.close();
}
SocketAddress socketAddress = new InetSocketAddress(ipAddress, port);
socket = new Socket();
socket.connect(socketAddress, 20000);
socket.setKeepAlive(true);
setupSSL(ipAddress, port);
} catch (IOException ex) {
throw new MxLightAgentException
("Error when creating socket connection. IP: " + ipAddress + " Port: " + port, ex);
}
}
断开套接字连接
public void disconnect() {
if (socket != null) {
try {
output.close();
incommingMessageProcessor.stopThread();
socket.close();
} catch (IOException e) {
LOGGER.info("disconnect", "Error on disconnection", e);
}
}
}
编写输出流
public boolean sendMessage(byte[] message) throws MxLightAgentException {
try {
LOGGER.info("Sending message via socket");
output = this.socket.getOutputStream();
byte[] len = DataConverter.toBytes(message.length);
output.write(len);
output.write(message);
output.flush();
LOGGER.info("Message sent and flushed");
return true;
} catch (IOException ex) {
throw new MxLightAgentException("Error when sending message to proxy.", ex);
}
}
我也实现了一个Netty套接字客户端,并得到了相同的结果。
即使网络连接断开,在写入输出流时也不会引发任何异常的原因是什么?
答案 0 :(得分:1)
TCP是基于字节的介质。它跟踪您发送的每个字节。报告SocketTimeoutException
之前需要等待一段时间。它可以自动从短暂的网络故障中恢复。您可以访问异常对象上的bytesTransferred
字段。这将为您提供保证已到达目的地的字节数。可能还有几个字节到达了目的地,但是绝对没有办法知道。
出于您的目的,您可以(1)跟踪消息的长度(2)实现消息级别ack
。
您有byte[] len = DataConverter.toBytes(message.length);
您可以拥有ArrayList
中的Integers
并将len
添加到该列表。现在,当发生异常时,您可以使用此列表来查找最后几条未送达的邮件。
ack
您可以同时使用套接字的InputStream
和OutputSteam
。在接收方,阅读消息后,您可以将回复发送回发送方。在发件人方面,您可以收听InputSteam
,当您收到回复时,您将知道您的消息已成功到达。
诚实地正确实现这些功能,尤其是#2,是一个可观的编程挑战。祝你好运!
答案 1 :(得分:0)
您应该定期进行自己的探测,以查看它是否仍然存在。 保留所有已发送消息的日志,这样,如果套接字确实断开连接,则可以列出服务器上已发送或未发送的内容。连接恢复在线后,您可以比较错过的内容并发送差额,或者只是再次发送所有差额。
关于您关于keepalive的评论的注释。 javaDoc指出(重点是我):
当为TCP套接字设置keepalive选项并且没有数据时 沿任一方向在插座上交换了2小时(注意: 实际值取决于实现),TCP会自动发送一个 保持对同伴的探测。
2小时是等待默认的“保持活动状态”检查的时间,因此您应该自己进行探测以查看其是否仍处于活动状态。
至于您的问题的答案: 您的服务器和客户端无法立即知道您的连接是否断开,除非内部发生故障。它不知道世界各地是否存在不良的网络连接,或者目标服务器是否存在令人难以置信的不良ping(10秒?),因此,它需要等待响应而不是直接抛出错误。离开。
您需要编写考虑这些问题的代码。如上所述,消息日志是一种处理方法。