Java套接字API:如何判断连接是否已closures?

我遇到了Java套接字API的一些问题。 我试图显示当前连接到我的游戏的玩家数量。 玩家连接时很容易确定。 但是,使用套接字API来确定玩家何时断开连接似乎并不是很困难。

在远程断开的套接字上调用isConnected()总是返回true 。 同样,在远程closures的套接字上调用isClosed()总是返回false 。 我已经阅读到,实际上确定一个套接字是否已经closures,数据必须写入输出stream,并且必须捕获一个exception。 这似乎是处理这种情况的一种非常不洁的方式。 我们只需要不断地通过networking发送垃圾信息来知道套接字何时closures。

有没有其他解决scheme?

没有TCP API会告诉你连接的当前状态。 isConnected()isClosed()告诉你套接字的当前状态。 不一样的事情。

  1. isConnected()告诉你是否连接了这个套接字 。 你有,所以它返回true。

  2. isClosed()告诉你是否closures了这个套接字。 直到你有,它返回false。

  3. 如果对方已经按顺序closures了连接

    • read()返回-1
    • readLine()返回null
    • readXXX()为任何其他XXX抛出EOFException

    • 写入操作会抛出一个IOException :“由对等端重置连接”,最终会受到缓冲延迟的影响。

  4. 如果由于其他原因导致连接丢失,写操作最终会抛出一个IOException ,如上所述,读操作可能会做同样的事情。

  5. 如果对方仍然连接,但没有使用连接,则可以使用读取超时。

  6. 相反,你可能在其他地方看到, ClosedChannelException不会告诉你这一点。 [ SocketException: socket closed. ]它只告诉你, closures了频道,然后继续使用它。 换句话说,你的一个编程错误。 它并不表示closures的连接。

  7. 作为在Windows XP上对Java 7进行的一些实验的结果,看起来如果:

    • 你正在selectOP_READ
    • select()返回一个大于零的值
    • 关联的SelectionKey已经无效( key.isValid() == false

    这意味着对等体已经重置了连接。 但是,这可能是JRE版本或平台所特有的。

在各种消息传递协议中,一直保持彼此的心跳(不断发送ping数据包),数据包不需要非常大。 探测机制将允许你检测断开连接的客户端,即使在TCP通常计算出来之前(TCP超时远高于此)发送一个探测器并等待5秒钟的回复,如果你没有看到2-3的回复随后的探测器,你的播放器被断开。

还有, 相关的问题

我看到刚刚发布的其他答案,但我认为你与玩游戏的客户互动,所以我可能会提出另一种方法(而BufferedReader在某些情况下肯定是有效的)。

如果您想…您可以将“注册”责任委托给客户。 也就是说,如果客户端超时,您可能会强制重新注册客户端,但这会导致下面的引用和想法。

我已经读过,实际上确定一个套接字是否已经closures,数据必须写入输出stream,并且必须捕获一个exception。 这似乎是处理这种情况的一种非常不洁的方式。

如果您的Java代码没有closures/断开Socket,那么您将如何通知远程主机closures了您的连接? 最终,你的try / catch实际上和在ACTUAL套接字上侦听事件的轮询者所做的事情基本相同。 考虑以下几点:

  • 您的本地系统可能会closures您的套接字而不会通知您…这只是套接字的实现(例如,它不轮询硬件/驱动程序/固件/任何状态改变)。
  • 新的套接字(代理p)…有多方(真正的6个端点),可能会closures你的连接…

我认为抽象语言的一个特点是你从细节中抽象出来。 想想SqlConnection的C#(try / finally)中的using关键字或者其他什么……这只是做生意的代价……我认为try / catch / finally是Socket使用的可接受和必要的模式。

我认为这是tcp连接的本质,在这个标准中,我们需要大约6分钟的传输沉默,然后才能断定连接已经结束! 所以我不认为你可以find这个问题的确切解决scheme。 也许更好的方法是写一些方便的代码来猜测服务器应该假设用户连接closures。

在Linux上,当你写入一个不知道你closures的套接字时,会激发一个SIGPIPE信号/exception,但是你想调用它。 但是,如果您不想被SIGPIPE抓住,则可以使用带有标志MSG_NOSIGNAL的send()。 send()调用将返回-1,在这种情况下,你可以检查errno,这将告诉你,你试图写一个损坏的pipe道(在这里是一个套接字)的值EPIPE,根据errno.h相当于32.作为对EPIPE的一个反应,你可以回头尝试重新打开socket并尝试再次发送你的信息。