我们面临一个奇怪的问题,即关闭一个死掉的 tcp 套接字(由拔掉电线引起)会影响另一个正常打开的 tcp 套接字。以下是详细信息:
拓扑
客户端 A ←→ 交换机 A ← 路由器 A:NAT ← .. 网络 .. → 路由器 B:NAT → 交换机 B ←→ 服务器 B问题:
假设客户端和服务器之间,由于拔掉电线而导致连接失效。拔掉电缆(机器和交换机之间)后,我们从另一台机器登录客户端 A,现在客户端和服务器之间将有一个新的 tcp 连接,这个连接是正常的。我们发现,从服务器端,如果我们在 tcp 内核还在重传数据的时候关闭了死掉的 tcp 连接,那么另一个 tcp 连接似乎被污染了,从客户端到服务器的方向将变得不可用,这意味着客户端发送的数据通过连接将永远不会被服务器接收,但令我们惊讶的是另一个方向 - 从服务器到客户端 - 仍然可以,通过服务器发送的相同 tcp 套接字数据到达客户端计算机。
但是如果我们等到死连接的tcp数据传输停止,比如2小时,然后关闭socket,那么另一个tcp连接仍然可以。
下面是这个问题的详细步骤:
1、有两个客户端,都在Router A后面:NAT,NAT是full-cone。
2、Router B:NAT后面有一个linux服务器,NAT是full-cone,但是这里使用端口转发。
3、四台机器,两个客户端说是X,Y,服务器说是S。
4. X 和 Y 登录并设置视频会议,现在他们都创建了到服务器的 tcp 连接,说他们是通道 CX 和通道 CY
5. 拔掉运行 Y 客户端的机器的电缆,现在通道 CY 坏了并且死了。但是通道 CX 仍然可以。
6. 在第四台机器上登录 Y 并再次与 X 建立视频会议,现在有一个新的 tcp 通道,说它是 CY2。
结果:
在第6步中,如果服务器关闭死连接——CY——在几分钟内,那么新的通道CY2将变成单向——从客户端Y发送的数据不能到达服务器,包括ACK数据包。好的,反之亦然。
如果服务器长时间关闭死连接 - CY - 2小时,则不会出现问题。
这个问题只发生在通过 NAT 运行时,至少我们在同一个 LAN 中运行应用程序时不会重现它(无需遍历 NAT)。
有人知道为什么会发生吗?
编辑:
在服务器端,我们使用非阻塞 tcp 套接字并选择模型。
psuedocode:
//server
listenfd = socket(,SO_STREAM,);
localAddr.port = htons(8013);
localAddr.ip = inet_addr(INADDR_ANY);
bind(localAddr...)
listen(listenfd, 100);
...
//using select model
select(maxFd, &fdSet, NULL, NULL);
for(...)
{
if (FD_ISSET(listenfd))
{
fd = accept(...)
set_non_block(fd);
...
}
...
}
更多信息:
1)第一台机器上的连接 A:192.168.10.4:13000 ←→ ... ← 路由器 A:NAT ← -现在:来自 PublicIP:8661(随机)..网络 .. → 路由器 B:NAT(到端口:8013, 端口转发) → ... ←→ 服务器 B
2)第二台机器上的连接 B:192.168.10.7:13000 ←→ ... ← 路由器 A:NAT ← -现在:从 PublicIP:8777(随机)..网络 .. → 路由器 B:NAT(到端口:8013,端口转发)→ ... ←→ 服务器 B
3) 拔掉电线,连接 A 已死,现在在第三台机器上创建一个新连接 C:192.168.10.10:13000 ←→ ... ← Router A:NAT ← -Now: from PublicIP:8869 (random).. Network ..→路由器B:NAT(到端口:8013,端口转发)→...←→服务器B
如果我们从服务器关闭连接 A,则连接 C 将变为单向,但如果我们在 2 小时内从服务器关闭连接 A,则连接 C 保持正常。