此示例使用C#编写,而不是Java编写,但是NAT遍历的概念与语言无关。
请参阅Michael Lidgren的网络库,其中内置了NAT遍历。
链接:http :
//pre.google.com/p/lidgren-network-gen3/ 处理NAT遍历的特定C#文件:http :
//pre.google.com/p/lidgren-network-
gen3/source/browse/主干/Lidgren.Network/NetNatIntroduction.cs
您发布的过程是正确的。在4种普通类型的NAT设备中,只有3种有效(我之所以说是通用的,因为NAT行为并未真正标准化):全锥NAT,受限锥NAT和端口受限锥NAT。NAT遍历不适用于对称NAT,对称NAT主要在公司网络中使用,以增强安全性。如果一方使用对称NAT而另一方不使用对称NAT,则仍然可以遍历NAT,但需要更多的猜测。从对称NAT到对称NAT的遍历非常困难-
您可以在此处阅读有关它的文章。
但是实际上,您描述的过程完全可以正常工作。我已经为我自己的远程屏幕共享程序(不幸的是,也使用C#)实现了它。只要确保已禁用Windows防火墙(如果使用Windows)和第三方防火墙即可。但是,是的,我可以很高兴地确认它会起作用。
澄清NAT遍历的过程
我正在写此更新,以为您和将来的读者阐明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。
IPv4地址已经用完了,它可以唯一地命名大约43亿台计算机。聪明的人预见到了这个问题,除其他原因外,他们还通过为连接到自身的1个共享IP地址分配了计算机网络,发明了路由器来抵抗IPv4地址耗尽。
有局域网IP。然后是WAN IP。LAN IP是局域网IP,用于唯一标识局域网中的计算机,例如连接到家庭路由器的台式机,笔记本电脑,打印机和智能手机。WAN
IP可以唯一地标识广域网中局域网外部的计算机-通常指Internet。因此,这些路由器为一组计算机分配了1个WAN IP。每台计算机仍具有自己的LAN
IP。当您键入
ipconfig命令提示符并获取时,将看到LAN IP
IPv4 Address . . . . . . . .192.168.1.101。当您连接
cmyip.com并获取WAN IP时,便会看到
128.120.196.204。
就像购买无线电频谱一样,代理商和组织也购买并保留整个IP范围,以及端口号。再次提醒您,我们没有多余的IPv4地址可供使用。
这与NAT遍历有什么关系?好吧,由于发明了路由器,因此直接连接(端到端连接)在某种程度上……是不可能的,没有一些黑客。如果您的网络由两台计算机(计算机A和计算机B)共享的WAN
IP组成
128.120.196.204,则连接连接到哪台计算机?我说的是外部计算机(例如google.com) 启动
与的连接
128.120.196.204。答案是: 没有人知道 ,路由器也不 知道 ,这就是路由器断开连接的原因。如果计算机A 启动
与的连接,例如
google.com,那就不同了。然后,路由器记住具有LAN IP的计算机A
192.168.1.101发起了与
74.125.227.64(google.com)。随着计算机A的请求数据包离开路由器,路由器实际上 将
LAN IP 重新写入
192.168.1.101路由器的WAN IP
128.120.196.204。因此,当google.com收到计算机A的请求数据包时,它会看到路由器重写的发件人IP,而不是计算机A的LAN
IP(google.com被
128.120.196.204视为要回复的IP)。当google.com最终答复时,数据包到达路由器,路由器 记住
(它有一个状态表)它正在等待google.com的答复,并适当地将数据包转发到计算机A。
换句话说,如果你的路由器有没有问题 ,你 的路由器会记得转发回复包返回到您的计算机(通过上述整个过程) -启动连接。但是,当外部服务器启动 与您
的连接 时 ,路由器将无法知道该连接的目标计算机,因为计算机A和计算机B都共享…的WAN
IP,
128.120.196.204除非有明确的规则指示路由器将所有最初转到目标端口的数据包转发到
X现在转到计算机A的目标端口的所有数据包
Y。这称为
端口转发
。不幸的是,如果您正在考虑将端口转发用于网络应用程序,则这是不切实际的,因为您的用户可能不了解如何启用它,并且如果他们认为这有安全隐患,可能不愿意启用它。
UPnP 只是指允许您以 编程方式启用端口转发的技术
。不幸的是,如果您正在考虑使用UPnP来转发您的网络应用程序,那么它也不切实际,因为UPnP并不总是可用,并且当启用时,默认情况下可能未打开。
那么解决方案是什么?解决方案是在您自己的计算机上代理全部流量(已将其预先配置为可全局访问),或者提出一种击败系统的方法。第一个解决方案(我相信)称为TURN,它以为服务器场提供可用带宽的价格神奇地解决了所有连接问题。第二种解决方案称为NAT遍历,这是我们接下来将要探讨的。
之前,我描述了外部服务器(例如google.com)发起与的连接的过程
128.120.196.204。我说过,如果路由器没有特定的规则来了解将google的连接请求转发到哪台计算机,则路由器会简单地断开连接。这是一般情况,由于存在不同类型的NAT,因此不准确。(注意:路由器是可以放在地板上的实际物理设备。NAT(网络地址转换)是编程到路由器中的软件过程,可帮助保存IPv4地址(如树)。因此,根据
该 NAT路由器使用,连接方案有所不同。路由器甚至可以 结合 NAT进程。
具有标准化行为的NAT有四种类型:全锥NAT,受限锥NAT,端口受限锥NAT和对称NAT。除了这些类型之外,还可以存在其他类型的具有非标准化行为的NAT,但这很少见。
注意:我对NAT并不是很熟悉…似乎有很多查看路由器的方法,并且互联网上的信息在此主题上非常广泛。Wikipedia说,已不赞成按完全,受限和端口受限的锥体对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的IP地址为192.168.1.101。计算机B的LAN
IP仍为192.168.1.102。计算机A和计算机B都将共享128.120.196.204的WAN IP。服务器S是服务器,是位于Amazon
EC2服务器上的全局可访问计算机,其WAN IP为1.1.1.1。服务器S可能具有LAN IP,但这无关紧要。计算机B也无关紧要。
计算机A从端口3000向服务器S发送一个数据包。在离开路由器的过程中,来自计算机A的数据包的源LAN IP被重新写入路由器的WAN
IP。路由器还将源端口300重写为32981。就外部IP和端口而言,服务器S看到什么?服务器S将128.120.196.204视为IP,而不是192.168.1.101,服务器S将32981视为端口,而不是3000。尽管这些不是原始IP,并且计算机A用来发送数据包的端口也正确,但这些都是正确的IP。和要回复的端口。收到数据包时,您只能知道WAN
IP和重写的端口。如果这就是您想要的(您只要求 外部 IP和端口),那么您就可以开始了。否则,如果您还想要发送者的内部IP, 与 标题 分开 。
码:
如上所述(下面 要回答 有关收集外部IP的问题),要收集每个客户端的外部IP和端口,只需从数据包中读取它们即可。发送的每个数据报 始终
具有发送方的源IP和源端口。您甚至不需要精美的自定义协议,因为始终包含这两个字段-根据定义,每个UDP数据包都必须具有这两个字段。
// Java language// Buffer for receiving incoming databyte[] inboundDatagramBuffer = new byte[1024];DatagramPacket inboundDatagram = new DatagramPacket(inboundDatagramBuffer, inboundDatagramBuffer.length);// Source IP addressInetAddress sourceAddress = inboundDatagram.getAddress();// Source portint sourcePort = inboundDatagram.getPort();// Actually receive the datagramsocket.receive(inboundDatagram);
因为
getAddress()和
getPort()可以根据目标端口或源端口的设置返回目标端口或源端口,所以在客户端(发送)机器上,调用
setAddress()并
setPort()返回到服务器(接收)机器,在服务器(接收)机器上,调用
setAddress()并
setPort()返回到客户端(发送)机器。必须有一种方法可以在中执行此 *** 作
receive()。请详细说明这是否是您的实际障碍(
getAddress()并且
getPort()不要返回您期望的源IP和端口)。这是假定服务器为“标准”
UDP服务器(不是STUN服务器)。
进一步更新:
我阅读了有关“ 如何使用STUN从一个客户端获取IP和端口并将其提供给另一个客户端
”的更新?STUN服务器并非旨在交换端点或执行NAT遍历。STUN服务器旨在告诉您您的公共IP,公共端口和NAT设备的类型(无论是全锥NAT,受限锥NAT还是端口受限锥NAT)。我将负责交换端点并执行实际NAT遍历的中间人服务器称为“介绍人”。在我的个人项目中,我实际上不需要使用STUN来执行NAT遍历。我的“介绍人”(介绍客户端A和B的中间人服务器)是侦听UDP数据报的标准服务器。当客户A和客户B都向介绍人注册时,介绍人会读取其公共IP以及端口和专用IP(如果它们位于LAN上)。从数据报头中读取公共IP,就像所有标准UDP数据报一样。专用IP被写入为数据报有效负载的一部分,而引入者只是将其读取为有效负载的一部分。因此,关于STUN的有用性,您无需依靠STUN来获取每个客户端的公共IP和公共端口-
任何连接的套接字都可以告诉您这一点。一世’
请详细说明障碍:如果您希望获得有关设计应用程序消息传递协议的最佳实践的建议,以及有关以有序和系统的方式(根据您在下面发表的评论)从接收到的消息中读取字段的建议,是否可以分享您的最新消息?方法?
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)