我们面临一个奇怪的问题,即关闭一个死tcp套接字(由拔掉电线引起)会影响另一个正常打开的tcp套接字。以下是详细信息:
拓扑
客户端A←→Switch A←路由器A:NAT←..网络..
→路由器B:NAT→交换机B←→服务器B
问题:
假设在客户端和服务器之间存在由拔出电线引起的死连接。拔下电缆(机器和交换机之间)之后,我们从另一台机器登录客户端A,现在客户端和服务器之间会有一个新的TCP连接,这个连接就可以了。
我们发现,从服务器,如果我们在tcp内核仍在重传数据时关闭死tcp连接,那么另一个tcp连接似乎会被污染,从客户端到服务器的方向将变得不可用这意味着客户端通过连接发送的数据永远不会被服务器接收,但令我们惊讶的是另一个方向 - 从服务器到客户端 - 保持正常,通过服务器发送的相同tcp套接字数据到达客户机。
但是如果我们等到死连接的tcp数据传输停止,例如。 2小时,然后关闭套接字,然后另一个tcp连接保持正常。
以下是此问题的详细步骤:
的 1。路由器A后面有两个客户端:NAT,NAT是全锥形。
的 2。路由器B后面有一个linux服务器:NAT,NAT是全锥形的,但这里它使用端口转发。
第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运行时才会出现此问题,至少我们在同一局域网内运行应用程序时不会重现它(无需遍历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,Port Forwarding)→...←→服务器B
2)第二台机器上的连接B:192.168.10.7:13000←→...←路由器A:NAT← - 现在:来自PublicIP:8777(随机)..网络..→路由器B:NAT (到端口:8013,端口转发)→...←→服务器B
3)拔掉电线并连接A已死,现在在第三台机器上创建一个新的连接C:192.168.10.10:13000←→...←路由器A:NAT← - 否则:来自PublicIP:8869 (随机)..网络..→路由器B:NAT(端口:8013,端口转发)→...←→服务器B
如果我们从服务器关闭连接A,那么连接C将成为单向,但如果我们在2小时内从服务器关闭连接A,则连接C仍然正常。
答案 0 :(得分:2)
NAT(尤其是全锥)通过为客户端提供内部地址(ip和端口)来工作,以匹配它尝试访问的外部地址。任何返回流量都会被发送到内部地址,然后由路由器转发到外部地址。
让我们用一个例子来扩展这个简短的探索,并展示这对你意味着什么......
假设您有一个NAT网关,将端口80转发到内部服务器,内部目标也是端口80。
网关具有外部IP n.n.n.n
和内部IP y.y.y.y
。
当客户端连接到n.n.n.n:80
时,NAT服务器忠实地将请求转发给y.y.y.y:80
,但在此过程中它重写了IP帧。发件人地址现在是NAT网关内部IP,发件人端口不再是客户端编写的内容,而是NAT网关分配的新端口。
新端口由NAT网关分配,是的。但它被指定为客户端IP及其尝试访问的端口的函数,在本例中为80。
一切顺利,但是......当客户端建立第二个连接时,使用相同的映射函数。这不应该造成问题吗?好吧,它可以。如果网关不区分不同的客户端地址(理想情况下,客户端的每个连接都应该有一个唯一的端口),它只会覆盖旧连接的映射。
因此导致重新发送来自旧套接字的流量发送到客户端新套接字。
非常不受欢迎,但可能取决于NAT的实现方式。而且因为它似乎是NAT的问题 - 它不会在直接连接时显示......
现在,我已经在我的解释中看到了一个漏洞 - 即这意味着您不能同时向同一台服务器打开两个套接字,因为任何返回都会出现乱码。好吧,我能想到的唯一原因是套接字仍处于打开状态 - 因此网关不会将其视为死信,然后为该客户端创建第二个映射。
希望我至少有某种意义。