2

我们面临一个奇怪的问题,即关闭一个死掉的 tcp 套接字(由拔掉电线引起)会影响另一个正常打开的 tcp 套接字。以下是详细信息:

  1. 拓扑
    客户端 A ←→ 交换机 A ← 路由器 A:NAT ← .. 网络 .. → 路由器 B:NAT → 交换机 B ←→ 服务器 B

  2. 问题:
    假设客户端和服务器之间,由于拔掉电线而导致连接失效。拔掉电缆(机器和交换机之间)后,我们从另一台机器登录客户端 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 保持正常。

4

1 回答 1

2

哇,真是个难题。我确实认为我有一个可能的答案。而且我不太喜欢其中的含义-但我想在查看标准时它们是不可避免的(这里是维基百科的简化)。

NAT(尤其是全锥体)通过给客户端一个内部地址(ip 和端口)来工作,以匹配它试图到达的外部地址。任何返回流量都会发送到内部地址,然后由路由器转发到外部地址。

让我们用一个例子来扩展这个简短的解释,并展示这对你意味着什么......

假设你有一个 NAT 网关,将 80 端口转发到内部服务器,内部目标也是 80 端口。网关有外部 IPn.n.n.n和内部 IP y.y.y.y

当客户端连接到n.n.n.n:80NAT 服务器时,它会忠实地将请求转发给y.y.y.y:80,但在此过程中它会重写 IP 帧。发送端地址现在是NAT网关内部IP,发送端端口不再是客户端写的,而是NAT网关分配的新端口。

新端口是由 NAT 网关分配的,是的。但它是根据客户端 IP 和它尝试访问的端口(在本例中为 80)分配的。

一切都很好,但是...当客户端建立其第二个连接时,将使用相同的映射功能。这不应该造成问题吗?那么它可以。如果网关不区分不同的客户端地址(理想情况下,来自客户端的每个连接都应该有一个唯一的端口),它将简单地覆盖旧连接的映射。

从而导致来自旧套接字的重新发送流量被发送到客户端的新套接字。

非常不受欢迎,但可能取决于 NAT 的实现方式。而且由于这似乎是NAT的问题-直接连接时不会显示...

现在,我已经在我的解释中看到了一个漏洞——也就是说,这意味着你不能同时向同一个服务器打开两个套接字,因为任何返回都会出现乱码。好吧,我能想到这行得通的唯一原因是套接字仍然打开 - 因此网关不会将其视为已死,然后为该客户端创建第二个映射。

希望我至少有点道理。

于 2013-03-18T22:12:24.680 回答