JAVA:处理套接字断开连接

Posted

技术标签:

【中文标题】JAVA:处理套接字断开连接【英文标题】:JAVA : Handling socket disconnection 【发布时间】:2012-08-27 22:46:49 【问题描述】:
    两台电脑通过socket连接。如果服务器/客户端关闭连接 从他们的最后(即关闭InputStreamOutputStreamSocket)那我该如何通知 关于断开的另一端?我知道一种方法 - 尝试从InputStream 读取, 如果连接关闭,它会抛出IOException,但是还有其他方法可以检测到吗? 还有一个问题,我在网上查了这个问题,看到inputStream.available() 不能解决这个问题。这是为什么呢?

附加信息:我要求另一种方式,因为如果我必须尝试从 InputStrem 检测断开连接。

【问题讨论】:

嗯,不知道您希望如何解决这个问题。您仍然需要某种类型的套接字读取或写入来确定它已关闭。 想想你怎么能做到这一点,以便在对方关闭(正确关闭,而不仅仅是断开连接)其套接字时即时通知:1)专用于读取套接字并放置数据的线程放入容器中以供将来处理,例如。数组阻塞队列。当它发现 -1 或 IOException 时,它可以停止并通过设置标志、调用某些方法或将 EOS 标记放入队列中来通知其他线程通信已经结束(或被中断)。 2)其他线程可以在需要的时候读取队列,也可以在需要的时候检查标志。 @SaintHill Allthat 对自己的阅读没有任何帮助。 @EJP 是的。这只是做不可避免的另一种方法:) 【参考方案1】:

试图从引发 IOException 的 InputStream 中读取数据

这是不正确的。如果对端关闭套接字:

read() 返回 -1 readLine() 返回 null readXXX() 抛出 EOFException,对于任何其他 X。

由于InputStream 只有read() 方法,它只返回-1:它不会在EOS 上抛出IOException

与此处的其他答案相反,没有 TCP API 或 Socket 方法可以告诉您对等方是否已关闭连接。您必须尝试读取或写入。

您应该使用读取超时。

InputStream.available() 不能解决问题,因为它不返回任何类型的 EOS 指示。它的正确用法很少,这不是其中之一。

【讨论】:

【参考方案2】:

当连接断开时,没有 O-O-O 方式来获取回调/异常。只有在对套接字流进行显式读/写时,才能了解断开的连接。

有两种从套接字读取的方法,即。在它们到达时逐字节同步读取;或等到流中可用的所需字节数,然后进行批量读取。您可以通过在套接字流上调用 available() 来进行检查,它会为您提供当前可供读取的字节数。在第二种情况下,如果套接字连接由于某种原因断开,则无法通知您。在这种情况下,您需要使用超时机制来等待。在您进行显式读/写的第一种情况下,您会遇到异常。

【讨论】:

O-O-O 与此无关。根本没有办法。调用available()的技巧没什么可推荐的。这只是一种更复杂、更容易出错的循环阻塞方式。【参考方案3】:

问题不在于“如果服务器/客户端关闭连接”。问题是“如果他们不关闭连接但连接断开了怎么办?”

如果没有您自己的心跳协议,就无法检测到这一点。

另一个选项是将 SO_KEEPALIVE 设置为 true。

“当为 TCP 套接字设置了 keepalive 选项并且在 2 小时内没有在任一方向上通过套接字交换数据时(注意:实际值取决于实现)”

根据我的经验,它比每 2 小时快得多。更像是大约 5 分钟。除了使用 So_KEEPALIVE 之外,你真的被搞砸了:P

在我的通信协议中,我使用每 2 秒发送一次的保留“心跳”字节。我自己的 filterInputStream 和 filterOutputStream 发送/消化心跳字节。

【讨论】:

我被你的回答笑了:D 设置 SO_KEEPALIVE 除非您执行读取或写入操作,否则如果对等端关闭连接,则它什么也不做。这不是 EOS 测试,它是一种检测中断的空闲连接的方法。 不,我明白了,我笑了“你真是被搞砸了:P”【参考方案4】:

Q1 如果你关闭服务器上的套接字连接,如果不是立即,客户端应该抛出异常,当然在下一次读取尝试时,反之亦然。

来自 JavaDocs 的 Q2

返回估计可以读取的字节数(或 跳过)从此输入流中没有被下一个阻塞 为此输入流调用方法。下一次调用 可能是同一个线程或另一个线程。单次阅读或跳过 这么多字节不会阻塞,但可以读取或跳过更少的字节。

这并不是流中当前字节数的指示,而是估计可以从不会阻塞当前线程的实现中读取的字节数

【讨论】:

Socket.isClosed() 不会告诉你对方是否已经关闭了他的连接端。它告诉你是否已经关闭了那个 Socket。 客户端不会抛出IOException。它可能抛出EOFException,具体取决于它调用的读取方法。 @EJP 因为EOFException 是从IOException 扩展而来的,所以它的作用几乎相同,具体取决于您要处理的异常级别。知道“可能”部分真是太棒了 如果被抛出它只是一样的东西,并且三个read()方法都没有抛出它。

以上是关于JAVA:处理套接字断开连接的主要内容,如果未能解决你的问题,请参考以下文章

如何判断套接字是不是已断开连接(Java)[重复]

c# - 处理关闭/断开 Web 套接字连接(CloseAsync 与 CloseOutputAsync)

用户连接和断开的消息处理

C# 客户端 - 服务器套接字断开处理

如何在socket.io中的断开事件上获取断开连接的客户端的套接字ID

Winsock - 客户端断开连接,关闭套接字循环/最大连接数