我正在实现一种算法,用于使用 TCP 打孔技术在两个客户端之间创建直接连接,该连接可能位于 NAT 后面。我在C++中使用WinSock2接口。
通过服务器交换外部地址后,客户端关闭连接(shutdown
使用SD_BOTH
和标志调用closesocket
)并尝试直接创建新连接(套接字绑定到以前使用的端口)。接下来,发生数据交换。
如果成功,TCP 数据包日志如下所示:
我在家庭网络上进行测试,两个客户端都在同一台计算机上运行。我认为,为了正确实施,应该没有区别。
192.168.153.196:52023 - 客户端 1、192.168.153.196 : 51722 - 客户端 2。在屏幕截图中,您可以看到客户端 1 发送的 SYN 段已成功传递到客户端 2,随后是 ACK 段,并开始通过路由器进行数据交换。在这种情况下,我们有一种 NAT,其中内部端口 = 外部端口(即,例如客户端 1 也从端口 52023 访问公共网络)。
当客户端无法相互建立连接时,请考虑以下日志:
192.168.1.106:52478 - 客户端 1、192.168.1.106 : 52477 - 客户端 2。接下来,日志中会重复发送一系列 SYN 段,直到客户端确信无法建立连接。显然,本例中未创建 NAT 映射。这里有一种不同类型的 NAT - 内部端口与外部端口不对应;客户端也运行在同一台机器上。
这是另一个不成功的示例,但已经在第一个示例的网络上(192.168.153.196:51640 - 客户端 1、192.168.153.196: 51639 - 客户端 2):
在这种情况下,据我了解,由于某种原因路由器重置了连接。
问题是,我做错了什么?每个客户端都知道另一个客户端的正确外部地址,在关闭与服务器的连接后短暂超时后,在大约相同的时间点,它们尝试从旧端口(调用)相互创建连接,但是路由器重置连接或不创建 NAT 条目connect
。
还有另一个奇怪的时刻。建立连接后,从交换开始最多几分钟,会出现以下情况:
随后几次尝试重新发送均不成功 - 数据包似乎被路由器忽略并且未转发。在这种情况下,调用send
不会显示错误,而recv
只是在接收方被阻止,因为它看不到数据。
此方法不适用于所有 NAT 类型,因为它们的行为未标准化。当两台主机通过传出连接相互连接时,处于同时打开TCP连接的状态,必须满足以下条件: