为了账号安全,请及时绑定邮箱和手机立即绑定

即使网络连接断开,TCP 套接字在发送消息时也不会抛出任何异常

即使网络连接断开,TCP 套接字在发送消息时也不会抛出任何异常

白衣非少年 2021-06-30 13:00:34
我用 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 套接字客户端并得到了相同的结果。即使网络连接断开,写入输出流时也不会抛出任何异常的原因是什么?
查看完整描述

2 回答

?
aluckdog

TA贡献1847条经验 获得超7个赞

TCP 是一种基于字节的媒体。它会跟踪您发送的每个字节。在报告之前等待一段时间SocketTimeoutException。它可以从短暂的网络故障中自动恢复。您可以访问bytesTransferred异常对象上的字段。这将为您提供保证已到达目的地的字节数。可能还有几个字节到达目的地,但绝对没有办法知道。

为了您的目的,您可以 (1) 跟踪您的消息长度 (2) 实现消息级别ack

(1) 跟踪您的消息长度

byte[] len = DataConverter.toBytes(message.length); 可以有一个ArrayListIntegers,并添加len到列表中。现在,当发生异常时,您可以使用此列表来了解有多少最后一条消息未送达。

(2) 实现消息层 ack

您可以同时使用套接字的InputStreamOutputSteam。在接收方,在阅读消息后,您可以向发送方发送回复。在发件人方面,您可以收听InputSteam,当您收到回复时,您将知道您的消息已成功送达。

老实说,正确地实现这些,尤其是#2,是一个可观的编程挑战。祝你好运!


查看完整回答
反对 回复 2021-07-14
?
翻阅古今

TA贡献1780条经验 获得超5个赞

您应该定期进行自己的探测,看看它是否还活着。记录所有已发送消息的日志,以便如果套接字确实断开连接,那么您将获得服务器上可能已发送或未发送的列表。当连接重新联机时,您可以比较丢失的内容并发送差异,或者简单地再次发送。

关于你对 keepalive 的评论的注释。javaDoc 指出,(强调我的):

当为 TCP 套接字设置了 keepalive 选项并且2 小时内没有在任一方向上通过套接字交换数据时(注意:实际值取决于实现),TCP 会自动向对等方发送一个 keepalive 探测。

2 小时是等待默认保持活动检查的很长时间,因此您应该自己进行探测以查看它是否还活着。

至于您的问题的答案:除非故障发生在内部,否则您的服务器和客户端无法立即知道您的连接是否已断开。它不知道地球另一端的网络连接是否有问题,或者目标服务器是否只是有一个非常糟糕的 ping(10 秒?),因此,它需要等待响应而不是直接抛出错误离开。

您需要编写考虑这些问题的代码。如上所述,消息日志是一种方法。


查看完整回答
反对 回复 2021-07-14
  • 2 回答
  • 0 关注
  • 261 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信