Mat*_* B. 35 java networking stun hole-punching
可以说我有两台电脑.
他们通过相互了解公共和私人IP ice4j
.
一个客户端正在侦听,另一个发送一些字符串.
我想通过UPD打孔来看到这种情况:
Let A be the client requesting the connection
Let B be the client that is responding to the request
Let S be the ice4j STUN server that they contact to initiate the connection
--
A sends a connection request to S
S responds with B's IP and port info, and sends A's IP and port info to B
A sends a UDP packet to B, which B's router firewall drops but it still
punches a hole in A's own firewall where B can connect
B sends a UDP packet to A, that both punches a hole in their own firewall,
and reaches A through the hole that they punched in their own firewall
A and B can now communicate through their established connection without
the help of S
Run Code Online (Sandbox Code Playgroud)
如果你考虑到双NAT也会很好.
您可以使用STUN来发现IP和端口,但您必须编写自己的代码,通过keepalive
技术将IP:端口发送到您的服务器.
一旦一个客户端通过服务器上的唯一ID识别另一个客户端,它将被提供另一个客户端IP:端口信息到UDP打孔它需要发送和接收的数据.
有一个库出现在java的地平线上检查出来:https:
//github.com/htwg/UCE#readme
Jas*_*son 52
此示例位于C#中,而不是Java中,但NAT遍历的概念与语言无关.
请参阅Michael Lidgren的内置NAT遍历的网络库.
链接:http://code.google.com/p/lidgren-network-gen3/ 具体的C#文件,NAT穿越处理:http://code.google.com/p/lidgren-network-gen3/source/browse/躯干/ Lidgren.Network/NetNatIntroduction.cs
您发布的流程是正确的.它会工作,对于只有3个4种一般类型的NAT设备(我说一般,因为NAT的行为是不是真的标准化):全锥形NAT的,限制锥的NAT和端口受限锥的NAT.NAT遍历不适用于Symmetric 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地址耗尽.
有LAN IP.然后是WAN IP.LAN IP是局域网IP,可以唯一地识别本地网络中的计算机,例如台式机,笔记本电脑,打印机和连接到家庭路由器的智能手机.WAN IP在广域网中唯一地识别局域网外的计算机 - 通常用于表示因特网.因此,这些路由器为一组计算机分配1个WAN IP.每台计算机仍然有自己的LAN IP.当您输入ipconfig
命令提示符并获取时,您会看到LAN IP 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的计算机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
.我说过,如果路由器没有特定的规则来了解哪台计算机转发谷歌的连接请求,路由器就会放弃连接.这是一个通用的场景,并不准确,因为有不同类型的NAT.(注意:路由器是您可以放在地板上的实际物理设备.NAT(网络地址转换)是一个编程到路由器中的软件进程,它有助于保存像树一样的IPv4地址.因此,根据该 NAT路由器使用,连接方案有所不同.路由器甚至可以组合 NAT进程.
有四种类型的具有标准化行为的NAT:全锥形NAT,受限锥形NAT,端口限制锥形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向端口6000的计算机B发送数据包.UDP数据包的源IP为192.168.1.101.这将是唯一的IP."外部"在这里没有上下文,因为网络纯粹是局域网.在此示例中,不存在广域网(如Internet).关于端口,因为我不确定NAT,我不确定数据包上刻的端口是否为3000. NAT设备可能会将数据包的端口从3000重新编写为随机的,如49826.无论哪种方式,你应该使用包装上的任何端口进行回复 - 这是您应该用来回复的内容.因此,在这个LAN通信示例中,您只需要发送一个IP - LAN IP,因为这一切都很重要.您不必担心端口 - 路由器会为您解决此问题.收到数据包后,只需从数据包中读取即可收集唯一的IP和端口.
WAN通信
计算机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的源端口.就外部IP和端口而言,Server 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 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);
Run Code Online (Sandbox Code Playgroud)
因为getAddress()
并且getPort()
可以返回目标端口或源端口,具体取决于您设置的内容,客户端(发送)机器,呼叫setAddress()
和setPort()
服务器(接收)机器,以及服务器(接收)机器,呼叫setAddress()
和setPort()
返回到客户端(发送)机器.必须有办法做到这一点receive()
.请详细说明这是否(getAddress()
并且getPort()
不返回您期望的源IP和端口)是您的实际障碍.这假设服务器是"标准"UDP服务器(它不是STUN服务器).
进一步更新:
我阅读了有关" 如何使用STUN从一个客户端获取IP和端口并将其交给另一个客户端 "的更新?STUN服务器不是为交换端点或执行NAT遍历而设计的.STUN服务器旨在告诉您公共IP,公共端口和NAT设备类型(无论是Full-Cone NAT,Restricted-Cone NAT还是端口限制锥形NAT).我打电话给中间服务器负责交换端点并执行实际的NAT遍历"介绍人".在我的个人项目中,我实际上不需要使用STUN来执行NAT遍历.我的"介绍人"(介绍客户端A和B的中间人服务器)是监听UDP数据报的标准服务器.由于客户A和B都向介绍人注册,介绍人会读取他们的公共IP和端口以及私有IP(如果他们在LAN上).与所有标准UDP数据报一样,公共IP从数据报头读取.专用IP是作为数据报有效负载的一部分编写的,介绍人只是将其作为有效负载的一部分读取.因此,关于STUN的用处,您不需要依赖STUN来获取每个客户端的公共IP和公共端口 - 任何连接的套接字都可以告诉您这一点.我说STUN仅用于确定客户端所使用的NAT设备类型,以便了解是否执行NAT遍历(如果NAT设备类型为Full-Cone,Restricted或Port-Restricted),或执行全部TURN流量代理(如果NAT设备类型是Symmetric).
请详细说明您的障碍:如果您需要有关设计应用程序消息传递协议的最佳实践的建议,以及有序和系统地阅读接收消息的字段的建议(基于您在下面发布的评论),您能否分享您的当前方法?
Yah*_*hia 10
你的问题非常广泛 - 我不能提供一个例子,但以下链接可能有所帮助(规格,库,样品等):
http://jstun.javawi.de/和http://www.t-king.de/blog/index.php?category=JSTUN(顺便说一下:JSTUN不再保留我收集的内容)
http://www.codeproject.com/Articles/23481/Stunner-A-STUN-Library-and-Client(使用C++,但可能会说明如何使其工作)
您的问题与Java无关.如果您知道如何打开UDP连接,那就足够了.阅读以下链接的内容.不要害怕标题,它也涵盖UDP.其余的只是Java编码.
PS:在您的方案中,缺少一步.A和B都必须与S打开连接,因为S需要告诉B A正试图到达它.如果B没有与S的开放连接,则A和B无法开始一起通信.
UPDATE
Jason的回答包含有关NAT遍历的错误和疯狂猜测.人们应该阅读Saikat Guha(mpi-sws.org/~francis/imc05-tcpnat.pdf)所做的工作来真正理解这件事.维基百科的锥形分类完全过时且具有误导性.
小智 6
STUN基本上如下工作:防火墙后面的客户端连接到防火墙外的STUN服务器.STUN服务器检查从客户端接收的数据包,并向客户端发回包含客户端IP和端口的响应,因为它们看起来像是STUN服务器.
这就是防火墙后面的客户端发现自己的外部IP和端口的方式.据我所知,STUN服务器通常不会将地址信息从一个客户端传递到另一个客户端.
通常,STUN用于通过防火墙设置媒体流,当防火墙已经对信令流量开放时 - 例如在VoIP中:客户端联系STUN服务器以发现其自己的外部IP和UDP流量端口,然后发送其信令请求( SIP INVITE或其他任何内容在一个众所周知的开放端口上 - 包括其在有效载荷中的外部UDP地址信息(SDP或其他).因此,通常需要通过开放端口可访问一个客户端以用于发送对等通信的信令.