JAVA:处理套接字断开连接
Posted
技术标签:
【中文标题】JAVA:处理套接字断开连接【英文标题】:JAVA : Handling socket disconnection 【发布时间】:2012-08-27 22:46:49 【问题描述】:-
两台电脑通过socket连接。如果服务器/客户端关闭连接
从他们的最后(即关闭
InputStream
、OutputStream
和Socket
)那我该如何通知
关于断开的另一端?我知道一种方法 - 尝试从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:处理套接字断开连接的主要内容,如果未能解决你的问题,请参考以下文章
c# - 处理关闭/断开 Web 套接字连接(CloseAsync 与 CloseOutputAsync)