在 NAT 后的两台计算机之间建立连接

Posted

技术标签:

【中文标题】在 NAT 后的两台计算机之间建立连接【英文标题】:Make a connection between two computers behind NAT 【发布时间】:2014-12-03 17:52:43 【问题描述】:

我正在尝试在 NAT 后面的两台计算机之间建立连接。我无法打开任何端口。我想使用一个可以上网并且能够与其他两个人交流的经纪人。之后,我想在这两者之间建立一条隧道。我正在使用 Python,我正在尝试编写这个代理。我该如何开始呢?

【问题讨论】:

有一百种方法可以做到这一点。 NAT 后面的计算机必须启动与常见 Internet 代理的连接。一个简单的实验选项是 zeromq,它有几个用于路由消息的工具。您甚至可以设置一个异步服务器,在两个服务器之间转发请求。 好吧,但是是否可以将服务器连接到代理,然后将客户端连接到该代理并在这两者之间建立隧道?无需再次使用代理 不,所有流量都必须通过代理。我假设这些机器位于不同的 NAT 后面,并且您不想在 NAT 后面进行端口转发。如果是这样,这些机器对彼此是不可见的,并且需要某种通用代理/代理进行通信。 奇怪。这就是 SIP 的工作方式,我感谢这将成为可能。 我不是 SIP 方面的专家,但如果 SIP 服务器无法将两个端点粘合在一起,它就会留在对话中。 SIP 节点具有广告其外部地址的协议,但它们仍然依赖于 NAT 端口转发(或伪装)。我不知道某些 NAT 在看到 SIP 流量时是否可以自动执行此操作,但步骤仍然存在。 【参考方案1】:

你想做的事情可能的,而且几乎是例行公事(至少对于 UDP)。这称为 NAT 打孔。它不适用于世界上的每个 NAT,但它适用于许多 NAT,包括大多数当前家用路由器的大多数设置。

基本思想是,对于大多数路由器,如果您发送一个传出数据包,它会将来自同一端点的所有传入数据包转发给您。否则,NAT 根本不起作用,对吧?因此,如果您和我都尝试同时与对方的公共地址通信,我们中的一个人将及时在他的 NAT 上“打一个洞”以接收消息。另一个人可能会错过最初的消息,因为他打洞太晚了,但他会收到另一个人发送的下一个回复,然后一切正常。

不过,这里有一些技巧。

首先,您需要知道您的公共地址。您可以直接看到的是您在 NAT 内的私人地址,因此您需要一个 NAT 外的服务器来告诉您您来自哪里。对此有一个标准,称为STUN。不仅有多种免费的实现你可以自己构建和运行,互联网上还有多个开放的 STUN 服务器;谷歌更新列表。

至少,你有一个私人地址和你的公共地址,你真的想把它们全部给另一个人,而不仅仅是一个。 (如果你只给我你的公共地址,而事实证明我们在同一个 NAT 上,我们可能无法相互通信。)如果你有多个 NIC,情况会变得更加复杂,或者你是在具有多层 NAT 等的公司 LAN 上。但是,如果您给我一大堆地址,我怎么知道该使用哪个?很简单,我可以尝试从我的每个本地地址连接到它们中的每一个,而第一个有效的是我一直使用的那个。我们显然需要为此制定某种协议,以便您可以告诉我其中一个何时起作用,但这可能非常简单。

另外,请注意,当我在这里说“地址”时,这不仅仅是指 IP 地址,而是指 IP 地址加上端口。毕竟,NAT 可以并且确实可以将端口与地址一起转换,并且您在 NAT 中打出的“洞”通常是特定于端口的。因此,您需要使用与实际通信相同的源端口和目标端口进行协商。 (实际上,这里有一些棘手的地方,在链接的文档中有解释,但现在让我们略读一下。)

当打孔不起作用并且您需要回退到通过服务器进行代理时,您不希望该代码看起来完全不同;你最终不得不把所有的东西都写两次,并且在调试方面遇到了很大的问题。幸运的是,有一个标准的解决方案,称为TURN,这使得对等方看起来就像他们实际上是在直接交谈,即使他们正在通过中继交谈。同样有免费的实现,但当然没有开放的 TURN 服务器供您使用(因为这会花费大量带宽)。

同时,一旦您知道了您的公共地址,您就必须告诉我它是什么,反之亦然。显然,我们需要一些边频道来交谈,比如特殊的“介绍人”或“大厅”服务器。通常,您会生成点对点连接作为某些服务器介导的连接的分支。例如,我们可能在一个 IRC 频道或 XMPP/Jabber 聊天网络中,并决定我们想要一个点对点通信(以避免窃听,或共享巨大的电子表格文件,或玩一个没有大量滞后)。

有一个名为ICE 的标准将这一切联系在一起;只要我们有一个共享的侧通道和一个 STUN(可能还有 TURN)服务器列表,ICE 就允许我们在可能的情况下协商点对点连接(如果没有,则回退到 TURN)。

ICE 支持是 SIP 和许多其他协议的一部分,因此,如果您使用其中一种协议的库,您可能只需要了解如何为其提供 STUN 和 TURN 服务器列表,这就是它。如果没有,可能有一些库可以做 ICE。如果没有,肯定有用于 STUN 和 TURN 的库(如果没有,它们都是非常简单的协议),因此您可以在它们之上自己进行协商。

当然,您不必使用这些标准中的任何一个,但您绝对应该阅读它们的作用以及它们这样做的原因。 (另外,请记住,人们现在专门制造路由器是为了让 ICE 更好地工作,而你发明的任何不同的东西都不是这样。)


我已经链接到上面的 Wikipedia 文章,因为它们从对技术如何工作及其背后的基本原理的非常简单的概述开始。对于详细的参考规范、入门示例代码、要使用的库等,有更好的资源,但通常 Wikipedia 提供了指向您需要的所有其他内容的链接。

【讨论】:

非常感谢您的回答。我有最后一个问题。我看到如何拥有所有客户的公共 ip。但是如何拥有端口? @MathieuLepage:好问题。因为 NAT 可以重新映射端口,所以您必须通过 STUN 获取您的公共端口,并通过您正在使用的任何侧通道或介绍器宣布这一点,就像地址一样。一些协议(包括 SIP)试图对相邻的端口范围进行欺骗,但许多 NAT 会破坏这些,所以如果你能避免它,那就去做吧。无论如何,如果您使用的是 ICE,它会与其余的协商一起处理这个问题。 但是冰可以在没有眩晕和转弯的情况下使用? @MathieuLepage:没用。 ICE 依靠 STUN 来寻找打孔地址,并依靠 TURN 来在打孔不起作用时设置继电器;如果两者都没有,它只能在本来可以直接连接的主机之间进行协商。 @MathieuLepage:我认为 ZeroC 的 Ice 和 ICE 完全不相关。如果你正在寻找一个好的库来为你做 ICE,你可能想看看Software Recs。我通过 GObject 使用过 libnice,并且听说过有关 PJSIP 实用程序及其 Python 绑定的好消息,但我能提供的仅此而已。

以上是关于在 NAT 后的两台计算机之间建立连接的主要内容,如果未能解决你的问题,请参考以下文章

通过外部服务器 SSH 防火墙后的两台计算机

通过IP地址与C#聊天程序。连接不同网络中的两台计算机

使用NAT方式联网的两台虚拟机为啥不能ping通。两台虚拟机分别在两台主机上,主机使用的是fedora14系统

udp打孔后发送文件

DDS 通信是不是可以在不同网络中的两台计算机之间进行?

使用python套接字在两台计算机之间进行通信