Java SocketChannel无法检测到断开连接?

8

我有一个正在运行的套接字,使用选择器。我正在尝试检查我的套接字是否已连接到服务器。

Boolean connected = _channel.isConnected();

不管怎样都返回true。我关闭了电脑的Airport(网络连接),但是当我检查套接字是否连接时,它仍然返回true。 有任何想法吗? 我每3秒尝试向服务器写入数据,但仍无法将我的套接字状态更改为断开连接。


我很困惑,有人不断地更改我的问题的标题。这里发生了什么? - aryaxt
通过上述代码,您正在检查SocketChannel是否仍然连接。那么,您到底是在测试什么,是问题中提到的Socket还是标题和代码中描述的通道?只是问一下,因为人们开始对我的答案进行投票...(没有解释为什么...) - Andreas Dolk
1
1- 你需要通过手动调用方法关闭socket,这就是为什么在与服务器断开连接后会返回false的原因。而我在这里试图检测是否存在断开连接问题,例如“连接丢失”,因此socket除非我尝试向服务器发送消息并收到异常,否则不会检测到其已断开连接。 2- 我还提到了我正在使用选择器(selector)。因此isConnected方法无法显示我是否已连接到服务器,但它可以显示我的socket是否仍连接到选择器,只要我的socket仍连接到选择器,它就会返回true。 - aryaxt
3个回答

12
通常情况下,如果关闭操作系统级别的网络连接,写入套接字会抛出异常,这样您就知道连接已中断。
然而更普遍的情况是,我们无法确定数据包是否被传送。在Java(可能也在C语言)中,没有办法检查数据包是否被确认。
即使我们可以检查TCP确认,它也不能保证服务器已经接收或处理了数据包。它只表示目标机器已经接收并将其缓存到内存中。此后还有很多事情可能会出错。
因此,如果你真的想要确信,你不能依赖传输协议。你必须拥有应用层确认,也就是说,服务器应用程序在从客户端接收和处理消息后返回一个ACK消息。
从客户端的角度来看,它向服务器发送一条消息,然后尝试从服务器读取ACK。如果成功接收到ACK,则可以确定其消息已被接收并处理。如果未能获取ACK,则无法知道发生了什么。实证上,最有可能的是TCP失败。下一个可能性是服务器崩溃。也可能是一切都很好,只是ACK无法到达客户端。

7
一个socket通道可以通过调用其connect方法连接;一旦连接上,socket通道将保持连接状态直到被关闭。
当服务器不再可用(由于物理连接断开或服务器故障),通道并不会关闭。因此,一旦建立了连接,在您关闭本地通道之前,isConnected()将返回true。
如果您想检查服务器是否仍然可用,请向套接字输出流发送一个字节。如果出现异常,则表示服务器不可用(连接丢失)。
编辑:
为EJP提供一些代码,以测试和重新考虑您的评论和答案:
public class ChannelTest {
     public static void main(String[] args) throws UnknownHostException, IOException {
      Socket google = new Socket(InetAddress.getByName("www.google.com"), 80);
      SocketChannel channel = SocketChannel.open(google.getRemoteSocketAddress());
      System.out.println(channel.isConnected());
      channel.close();
      System.out.println(channel.isConnected());
    }
}

我的电脑上的输出结果是

true
false

2
亲爱的投票者们 - 如果您认为答案不够有帮助,请留下评论。至少EJP的评论是不正确的,因为他的回答...问题是关于SocketChannels的,我仍然确定,这解释了为什么关闭机场不会导致isConnected() == false - Andreas Dolk
1
我同意Andreas的观点:未经解释的负分评价是毫无用处的,甚至更糟。我开始将未经解释的负分评价视为纯粹的网站破坏行为。 - user207421

4

isConnected()函数告诉你当前的通道对象是否已经连接,这个对象是你自己创建的,但是它没有指定在关闭后是否返回false,虽然显然它会返回false:详见安德烈亚斯的回答。它并不能告诉你底层的连接是否仍然存在。你只能通过使用它来判断:从读取中得到-1或者异常,这样可以知道连接已经断开。


当我关闭到远程服务器的通道时,isConnected()在我的机器上返回false。请参见我的答案以获取一个可行的示例。 - Andreas Dolk
read 返回的 -1 也不起作用。我添加了一个 read,它被阻塞(等待字节)。然后我拔掉了网络电缆,应用程序仍然被阻塞(没有 -1 作为结果)。 - Andreas Dolk
是的,从读取中返回的-1表示有序关闭。如果使用读取而没有写入,则检测无序关闭的唯一方法是通过读取超时,该超时会引发异常。 - user207421

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接