Apache Mina,如何检测何时使用无效套接字向客户端发送消息?

Apache Mina, How to detect when you're sending messages using an invalid socket to the client side?

我有一个使用 MINA 版本 2 的服务器设置。 我对套接字和 tcp 没有太多经验。

问题是如果我连接到我的服务器,然后拔下我的互联网并关闭连接,(服务器没有收到连接关闭的通知)服务器将永远认为我的连接仍然存在有效且有效。

服务器将继续向我的连接发送消息,并且不会抛出任何异常,即使我的计算机上没有任何内容绑定到本地端口。

如何测试连接是否仍然存在?

我试过 运行 MINA 在调试模式下登录,并记录

 IoSession.isConnected() IoSession.isActive IoSession.isClosing

他们总是return对,对,错。此外,在调试模式下,没有有用的信息表明连接已丢失。它只是记录了常规的“已发送消息”内容,就好像没有任何问题一样。

在使用 Flash actionscript 时,我有过 flash 在无效套接字上运行时会抛出错误的经历。这让我相信这是在说服务器上的套接字不再对连接有效。所以换句话说,如果闪存可以检测到无效的套接字,Java 服务器应该能够检测到它是否正确?

如果真的没有办法检测死连接,我总是可以设置一个连接保持活动例程,客户端不断向服务器发送“我在这里”消息,服务器关闭没有会话有几秒钟的传入消息。


编辑:在了解到“套接字”是私有的并且从不在网络上共享之后,我设法为我的问题找到了更好的结果,并且我找到了这个 SO 线程。

Java socket API: How to tell if a connection has been closed?

很遗憾

IOException 'Connection reset by peer' Doesn't occur when I write to the IoSession in MINA.


编辑:

在 Java 中是否有任何方法可以检测在发送数据包后何时未收到对 TCP 数据包的 ACK? ACK 超时?


编辑:

但是显然,我的计算机应该向服务器发送 RST?根据这个答案。 但这似乎是一种糟糕的端口扫描方式。这是端口扫描的工作原理吗?端口扫描器将数据发送到端口,受害者的服务以 RST 响应?抱歉,我想我需要一个新问题来解决所有这些问题。但奇怪的是,MINA 在发送数据时并没有抛出 connection reset by peer。那么我的电脑不会发送RST。

Internet 协议中的 socketconnection 的概念是一种错觉。这是操作系统和 TCP 栈提供给你的一个方便的抽象,但实际上,这都是假的。

在幕后,Internet 上的所有内容都采用单独的 数据包 的形式。

从一台计算机向另一台计算机发送数据包的角度来看,没有内置方法可以知道该计算机是否实际接收了数据包,除非该计算机(或介于两者之间的其他计算机,如路由器)告诉您数据包已收到或未收到。

从一台计算机期望从另一台计算机接收数据包的角度来看,没有办法提前知道是否有数据包到来,是否会到来,或者以什么方式到来订单——直到他们实际到达。一旦它们到达,您收到一个包裹这一事实并不意味着您将来会收到更多。

这就是为什么我说连接或套接字是一种幻觉。操作系统确定连接是否为 "alive" 的方式是简单地等待任意时间。在这段时间之后——称为 timeout——如果 TCP 连接的一侧没有收到另一端的回音,它将假定另一端已断开连接, 任意设置连接状态为 "closed", "dead" 或 "terminated" ("timed out").

所以:

  • 您的服务器不知道您已经断开了 Internet 连接。它无法知道这一点。
  • 您的服务器的 TCP 堆栈已配置为在 "giving up" 未收到响应的情况下在另一端之前等待任意时间。如果此超时设置为非常长的时间段,您可能会觉得您的服务器挂在不再有效的连接上。如果这让您感到困扰,您应该研究减少超时间隔的方法。

打个比方:如果您正在与某人通phone 电话,而他们确实存在受伤或死亡的风险,而您正在与他们交谈并让他们接听电话,然后phone 突然死了..... 好吧,你等多久?你认为对方在什么时候受伤或死亡?如果您等待几毫秒,在大多数情况下 太短 "timeout",因为其他人可能只是在倾听并思考如何回应。如果再等50年,那人可能早就死了。所以你必须设置一个合理的超时值。

您想要的是 KeepAlive、心跳或 ping。

根据@allquicatic 的回答,在 TCP 中没有完全可靠的内置方法来执行此操作。您必须实现一种方法来显式询问客户端 "Are you still there?" 并在指定的时间内等待答案。

https://en.wikipedia.org/wiki/Keepalive

A keepalive (KA) is a message sent by one device to another to check that the link between the two is operating, or to prevent this link from being broken.

https://en.wikipedia.org/wiki/Heartbeat_(computing)

In computer science, a heartbeat is a periodic signal generated by hardware or software to indicate normal operation or to synchronize other parts of a system.[1] Usually a heartbeat is sent between machines at a regular interval in the order of seconds. If a heartbeat isn't received for a time—usually a few heartbeat intervals—the machine that should have sent the heartbeat is assumed to have failed.[2]

最简单的实现方式是定期发送任意数据 - 例如一个空命令。如果在指定的超时期限内未收到 ACK,则正确编程的 TCP 堆栈将超时,然后您将获得 IOException 'Connection reset by peer'

如果您想要比默认超时更细粒度的控制,您可能需要手动调整 TCP 参数,或者实现您自己的功能。

TCP 框架未公开 Java。并且 Java 不提供编辑存在于 OS 级别的 TCP 配置的方法。

这意味着我们无法在 Java 中有效地使用 TCP keep alive,因为我们无法更改其默认配置值。此外,我们无法设置未收到已发送消息的 ACK 的超时。 (学习TCP发现每条发送的消息都会等待对方的ACK(确认)消息已经成功传递。)

Java 只能在以下情况下抛出异常,例如在自定义时间内未完成 TCP 握手的超时,从对等方接收到 RST 时的 'Connection Reset by Peer' 异常,以及在可能的任何时间段之后发生 ACK 超时的异常。

要可靠地跟踪连接状态,您必须实现自己的 Ping/Pong、Keep Alive 或 Heartbeat 系统,正如@Dog 在他的回答中所建议的那样。 (服务器必须轮询客户端以查看它是否仍然存在,或者客户端必须不断地让服务器知道它仍然存在。)

例如,将您的客户端配置为每 10 秒发送一个小数据包。 在 MINA 中,你可以设置一个 session reader idle timeout,当一个 session reader 空闲一段时间后会发送一个事件。您可以在传递此事件时终止该连接。将 reader 超时设置为比小数据包间隔长一点将说明客户端和服务器之间的随机高延迟。例如,在这种情况下,reader 空闲超时 15 秒就可以了。

如果您的服务器很少遇到会话空闲,并且您认为可以通过在会话空闲时轮询客户端来节省带宽,请考虑使用 Apache MINA Keep Alive Filter。

https://mina.apache.org/mina-project/apidocs/org/apache/mina/filter/keepalive/KeepAliveFilter.html