这个例子是在 C# 中,而不是在 Java 中,但是 NAT 遍历的概念与语言无关。
请参阅 Michael Lidgren 的内置 NAT 遍历的网络库。
链接:http ://code.google.com/p/lidgren-network-gen3/
处理 NAT 遍历的特定 C# 文件:http ://code.google.com/p/lidgren-network-gen3/source/browse/主干/Lidgren.Network/NetNatIntroduction.cs
您发布的过程是正确的。它只适用于 4 种通用类型的NAT 设备中的 3 种(我说通用是因为 NAT 行为并未真正标准化):Full-Cone NAT、Restricted-Cone NAT 和 Port-Restricted-Cone NAT。NAT 遍历不适用于对称 NAT,对称 NAT 主要存在于公司网络中以增强安全性。如果一方使用对称 NAT 而另一方不使用,仍然可以穿越 NAT,但需要更多猜测。对称 NAT 到对称 NAT 的穿越非常困难 -您可以在此处阅读有关它的论文。
但实际上,您描述的过程完全有效。我已经为我自己的远程屏幕共享程序(不幸的是,也在 C# 中)实现了它。只需确保您已禁用 Windows 防火墙(如果您使用的是 Windows)和第三方防火墙。但是,是的,我可以很高兴地确认它会起作用。
阐明 NAT Traversal 的过程
我编写此更新是为了向您和未来的读者阐明 NAT 遍历的过程。希望这可以是对历史和过程的清晰总结。
部分参考来源:http ://think-like-a-computer.com/2011/09/16/types-of-nat/和http://en.wikipedia.org/wiki/Network_address_translation , http:// en.wikipedia.org/wiki/IPv4,http://en.wikipedia.org/wiki/IPv4_address_exhaustion。_ _
能够唯一命名大约 43 亿台计算机的 IPv4 地址已经用完。聪明的人预见到了这个问题,并且除其他原因外,发明了路由器来对抗 IPv4 地址耗尽,方法是为连接到自身的计算机网络分配一个共享 IP 地址。
有 LAN IP。然后是 WAN IP。LAN IP 是局域网 IP,可唯一标识本地网络中的计算机,例如连接到家庭路由器的台式机、笔记本电脑、打印机和智能手机。WAN IP 在广域网中唯一地标识局域网之外的计算机 - 通常被认为是指 Internet。因此这些路由器为一组计算机分配了 1 个 WAN IP。每台计算机仍然有自己的 LAN IP。LAN IP 是您在输入ipconfig
命令提示符并获取IPv4 Address . . . . . . . . 192.168.1.101
. WAN IP 是您在连接cmyip.com
并获取128.120.196.204
.
正如无线电频谱被买断一样,整个 IP 范围也被机构和组织以及端口号买断和保留。简短的信息是,我们没有更多的 IPv4 地址可用。
这与 NAT 穿越有什么关系?好吧,自从发明了路由器以来,直接连接(端到端连接)在某种程度上......不可能,没有一些黑客攻击。如果您有一个由 2 台计算机(计算机 A 和计算机 B)组成的网络,它们都共享 的 WAN IP 128.120.196.204
,连接到哪台计算机?我说的是一台外部计算机(比如 google.com)启动与128.120.196.204
. 答案是:没有人知道,路由器也不知道,这就是路由器断开连接的原因。如果计算机 A发起与 的连接google.com
,那么情况就不同了。然后路由器会记住具有 LAN IP 的计算机 A192.168.1.101
发起了一个连接到74.125.227.64
(谷歌网站)。随着计算机A的请求包离开路由器,路由器实际上将LAN IP重新写入192.168.1.101
路由器的WAN IP 128.120.196.204
。因此,当 google.com 收到计算机 A 的请求数据包时,它看到的是路由器重写的发件人 IP,而不是计算机 A 的 LAN IP(google.com128.120.196.204
视为要回复的 IP)。当 google.com 最终回复时,数据包到达路由器,路由器记住(它有一个状态表)它期待来自 google.com 的回复,并适当地将数据包转发到计算机 A。
换句话说,当您启动连接时,您的路由器没有问题 - 您的路由器会记住将回复数据包转发回您的计算机(通过上述整个过程)。但是,当外部服务器向您发起连接时,路由器无法知道该连接是针对哪台计算机的,因为计算机 A 和计算机 B 都共享128.120.196.204
...的 WAN IP,除非有明确的规则指示路由器将所有最初发往目的端口X
的数据包转发到计算机 A,目的端口Y
。这称为端口转发. 不幸的是,如果您正在考虑为您的网络应用程序使用端口转发,这是不切实际的,因为您的用户可能不了解如何启用它,并且如果他们认为这是一个安全风险,他们可能不愿意启用它。UPnP只是指允许您以编程方式启用端口转发的技术。不幸的是,如果您正在考虑使用 UPnP 来端口转发您的网络应用程序,那么它也不实用,因为 UPnP 并不总是可用的,而且当它可用时,它可能不会默认打开。
那么解决方案是什么呢?解决方案是通过您自己的计算机代理您的全部流量(您已经仔细预配置为全局可访问),或者想出一种击败系统的方法。第一个解决方案(我相信)称为TURN,它以提供可用带宽的服务器场为代价神奇地解决了所有连接问题。第二种解决方案称为 NAT 遍历,这就是我们接下来要探索的内容。
早些时候,我描述了外部服务器(比如 google.com)发起与128.120.196.204
. 我说过,如果路由器没有特定的规则来了解将 google 的连接请求转发到哪台计算机,路由器就会简单地断开连接。这是一个笼统的场景,并不准确,因为有不同类型的 NAT。(注意:路由器是您可以放在地板上的实际物理设备。NAT(网络地址转换)是一个编程到路由器中的软件过程,有助于保存 IPv4 地址,如树)。因此,根据路由器使用的NAT,连接方案会有所不同。路由器甚至可以组合NAT 进程。
具有标准化行为的 NAT 有四种类型:Full-Cone NAT、Restricted-Cone NAT、Port-Restricted-Cone NAT 和对称 NAT。除了这些类型之外,还可以有其他类型的具有非标准化行为的 NAT,但这种情况比较少见。
注意:我对 NAT 不太熟悉……似乎有很多方法可以查看路由器,并且互联网上的信息在这个主题上非常分散。维基百科说,通过完整、受限和端口受限的锥体对 NAT 进行分类已经有些过时了?有一种叫做静态和动态 NAT 的东西……只是一堆我无法协调的各种概念。不过,以下模型适用于我自己的应用程序。您可以通过阅读下面和上面的链接以及整篇文章来了解更多关于 NAT 的信息。我不能发布更多关于他们的信息,因为我对他们不太了解。
希望一些网络专家更正/添加输入,以便我们都可以更多地了解这个神秘的过程。
要回答有关收集每个客户端的外部 IP 和端口的问题:
所有 UDP 报文的头部结构相同,一源 IP,一源端口。UDP 数据包标头不包含“内部”源 IP 和“外部”源 IP。UDP 数据包头只包含一个源 IP。如果您想获得“内部”和“外部”源 IP,则需要实际发送内部源 IP 作为有效负载的一部分。但听起来您不需要内部源 IP 和端口。正如您的问题所述,听起来您只需要一个外部 IP 和端口。这意味着您的解决方案只需读取源 IP 并将数据包从数据包中取出,就像它们的字段一样。
以下两种情况(他们并没有真正解释其他任何事情):
局域网通讯
计算机 A 的 LAN IP 为 192.168.1.101。计算机 B 的 LAN IP 为 192.168.1.102。计算机 A 从端口 3000 向计算机 B 的端口 6000 发送一个数据包。UDP 数据包上的源 IP 将为 192.168.1.101。这将是唯一的 IP。“外部”在这里没有上下文,因为网络纯粹是局域网。在此示例中,不存在广域网(如 Internet)。不过关于端口,因为我不确定 NAT,所以我不确定数据包上的端口是否为 3000。NAT 设备可能将数据包的端口从 3000 重新写入随机端口,如 49826。无论哪种方式,您都应该使用数据包上记录的任何端口来回复 - 这就是您应该用来回复的端口。因此,在这个 LAN 通信示例中,您只需要发送一个 IP - LAN IP,因为这才是最重要的。您不必担心端口 - 路由器会为您处理。当您收到数据包时,您只需从数据包中读取它即可收集唯一的 IP 和端口。
广域网通信
计算机 A 的 LAN IP 还是 192.168.1.101。计算机 B 的 LAN IP 还是 192.168.1.102。计算机 A 和计算机 B 将共享一个 WAN IP 128.120.196.204。服务器 S 是一台服务器,一台全球可访问的计算机,比方说,一台 Amazon EC2 服务器,其 WAN IP 为 1.1.1.1。服务器 S 可能有一个 LAN IP,但这无关紧要。计算机 B 也无关紧要。
计算机 A 从端口 3000 向服务器 S 发送一个数据包。在离开路由器的途中,计算机 A 的数据包源 LAN IP 被重新写入路由器的 WAN IP。路由器也将300的源端口改写为32981。Server S看到的外部IP和端口是什么?服务器 S 将 128.120.196.204 视为 IP,而不是 192.168.1.101,服务器 S 将 32981 视为端口,而不是 3000。虽然这些不是计算机 A 用于发送数据包的原始 IP 和端口,但这些是正确的 IP和要回复的端口。收到包时,只能知道WAN IP和改写的端口。如果这就是你想要的(你只要求外部IP 和端口),那么你就设置好了。否则,如果您还想要发件人的内部 IP,与您的标题分开。
代码:
如上所述(下面要回答有关收集外部 IP 的问题),要收集每个客户端的外部 IP 和端口,您只需从数据包中读取它们。每个发送的数据报总是有发送者的源IP和源端口;您甚至不需要花哨的自定义协议,因为这两个字段总是包含在内 - 根据定义,每个单独的 UDP 数据包都必须具有这两个字段。
// Java language
// Buffer for receiving incoming data
byte[] inboundDatagramBuffer = new byte[1024];
DatagramPacket inboundDatagram = new DatagramPacket(inboundDatagramBuffer, inboundDatagramBuffer.length);
// Source IP address
InetAddress sourceAddress = inboundDatagram.getAddress();
// Source port
int sourcePort = inboundDatagram.getPort();
// Actually receive the datagram
socket.receive(inboundDatagram);
因为getAddress()
andgetPort()
可以返回目标或源端口,具体取决于您将其设置为,在客户端(发送)机器上,调用setAddress()
和setPort()
到服务器(接收)机器,在服务器(接收)机器上,调用setAddress()
和setPort()
返回到客户端(发送)机器。必须有办法做到这一点receive()
。请详细说明这(getAddress()
并且getPort()
不要返回您期望的源 IP 和端口)是否是您的实际障碍。这是假设服务器是“标准”UDP 服务器(它不是 STUN 服务器)。
进一步更新:
我阅读了您关于“如何使用 STUN 从一个客户端获取 IP 和端口并将其提供给另一个客户端”的更新?STUN 服务器并非旨在交换端点或执行 NAT 遍历。STUN 服务器旨在告诉您您的公共 IP、公共端口和 NAT 设备的类型(无论是 Full-Cone NAT、Restricted-Cone NAT 还是 Port-Restricted Cone NAT)。我将负责交换端点和执行实际 NAT 遍历的中间人服务器称为“介绍人”。在我的个人项目中,我实际上不需要使用 STUN 来执行 NAT 遍历。我的“介绍人”(介绍客户端 A 和 B 的中间人服务器)是侦听 UDP 数据报的标准服务器。当客户端 A 和 B 都向介绍人注册时,介绍人会读取他们的公共 IP 和端口以及私有 IP(如果他们在 LAN 上)。与所有标准 UDP 数据报一样,从数据报头中读取公共 IP。私有 IP 作为数据报有效负载的一部分写入,引入者只是将其作为有效负载的一部分读取。所以,关于 STUN 的用处,你不需要依赖 STUN 来获取每个客户端的公共 IP 和公共端口——任何连接的套接字都可以告诉你这一点。我'
请详细说明您的障碍:如果您需要有关设计应用程序消息传递协议的最佳实践的建议,以及有关以有序和系统的方式阅读收到的消息的字段的建议(基于您在下面发布的评论),您能否分享您当前的方法?