如何重现静默断开的 TCP/IP 连接?

Posted

技术标签:

【中文标题】如何重现静默断开的 TCP/IP 连接?【英文标题】:How to reproduce a silently dropped TCP/IP connection? 【发布时间】:2011-09-01 06:19:16 【问题描述】:

我有一种情况,Java 程序与服务器建立了一个长时间运行的 TCP/IP 连接,并且该连接的功能与记录的完全一样,除了我无法控制的环境中的一件事。

每个星期六该服务器都会重新启动,但由于某种原因,这没有正确地传达给我的客户端,因此连接一直挂起等待响应。这与陈旧的 JDBC 连接所见的行为完全相同,服务器和客户端之间的某些路由器发现连接空闲并在没有通知的情况下将其丢弃。

我需要能够重现此场景才能创建正确的修复程序。

因此我需要知道是否有一种好方法可以模拟路由器在我的控制下静默断开连接?

我可以在我的开发环境和服务器之间放置一个 Linux 机器或 Mac。如果我可以在虚拟机(vmware 播放器)中进行放置,我可以在虚拟机中运行 JVM。

非常欢迎提出建议。


编辑 2012-12-18:对我有用的解决方案是在本地使用基于 Java 的 SOCKS5 代理,可以随意使用 Ctrl-Z 暂停,并告诉我的应用程序通过它。

【问题讨论】:

您是否在客户端和服务器的套接字上使用 TCP Keepalives(SO_KEEPALIVE 选项)? @Mike,可能不会。套接字代码在第三方库中。 好吧,如果客户端无限期挂起是不可接受的,听起来keepalives会很有用。 正确的解决方法是让客户端设置 TCP keepalive,如果您不介意等待两个小时,或者使用更实际的超时值调用 Socket.setSoTimeout(),例如几分钟。 @Mike,很可能,但不幸的是我无法更改所述第三方库。 【参考方案1】:

您应该能够使用 iptables 临时应用规则来丢弃来自远程服务器 (a.b.c.d) 的所有数据包。类似于:

iptables -A INPUT -s a.b.c.d -j DROP

当你想关闭过滤器时

iptables -D INPUT -s a.b.c.d -j DROP

您也可以使用这些规则来丢弃其他特定的数据包,但我认为这将准确地模拟您的客户端软件在服务器消失时所看到的内容。

但请注意,当您重新打开连接时,远程服务器可能仍然拥有旧连接,因此这不会完美地模拟重新启动。

【讨论】:

或者在 Windows 防火墙建立后的某个时间阻止 TCP 连接 这不适用于已经建立的连接。【参考方案2】:

使用Socat 转发您的连接(很容易设置为 TCP 转发器/代理),即使您愿意也可以在同一台机器上(通过引入一个新端口供代理运行并将其指向'官方'端点)

查看simple TCP forwarder 示例

然后,当您想测试中断时,您可以杀死它(并且套接字将被关闭)或可能尝试停止进程(CTRL Z linux)以查看您的目标如何处理停滞的连接。

有什么问题吗?我会尽力回答......祝你好运!

【讨论】:

我熟悉 socat - 我通过 SOCKS 代理将它用于远程桌面。 原来场景涉及多个网络端口,使用JSocks(jsocks.sourceforge.net)比socat更容易正确处理,因为-DsocksProxyHost=...系统属性透明地指示JVM使用袜子代理。不过我使用了 Ctrl-Z 技巧 - 效果很好。 啊酷!感谢分享,下次我玩连接逻辑时我会检查一下:) 我在 Windows 7 上使用 Java 7 尝试了 JSocks。它很容易使用,但在被杀死时会立即触发“SocketException:连接重置”。所以在我的情况下,静默断开连接是不合适的。 我得到了与 socat 相同的结果,“连接重置”。【参考方案3】:

基于原生 Linux 的解决方案是使用 tc 和 netem。 Netem 是一个特殊的队列,可以附加到各种网络接口上,从而导致延迟、重新排序和丢失(详见网页)。 netem 的一些使用示例包括:

tc qdisc change dev eth0 root netem loss 0.1%

使 eth0 丢弃所有传出数据包的 0.1%,并且

tc qdisc change dev eth0 root netem loss 0.3% 25%

创建突发丢包:

这将导致 0.3% 的数据包丢失,并且每个连续的概率取决于最后一个的四分之一。

如果您还需要丢弃传入的数据包,您可以使用ifb(中间功能块设备)。您设置它,将 eth0 收到的所有数据包转发到 ifb,并将 netem 附加到 ifb 以造成丢失或延迟。有关详细信息,请参阅 ifb 的文档。

【讨论】:

【参考方案4】:

从您的计算机上拔下网线。

只对这个模块进行一个简单的测试。连接到服务器并进入等待部分。然后,拔出网络插头。

当然,重要的是隔离此模块并在运行此测试时仅显示此模块。您的程序可能还有其他方面需要网络访问,而这些不应该运行。

【讨论】:

以上是关于如何重现静默断开的 TCP/IP 连接?的主要内容,如果未能解决你的问题,请参考以下文章

学习TCP/IP - TCP三次握手连接和四次握手断开连接

如何判断客户端是不是如何判断是不是与服务器断开了

TCP/IP网络编程:07优雅地断开套接字连接

TCP/IP 协议

C# TcpListener如何知道客户端已经断开连接

TCP/IP