Java UDP打孔示例 – 通过防火墙连接

可以说我有两台电脑。

他们通过ice4j彼此了解公共和私人IP。

一个客户端正在监听,另一个正在发送一些string

我想通过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 

有没有人可以通过对称NAT来伪造如何去打孔的伪示例? 假设将有服务器S,这将有助于猜测端口号,并build立客户端A和B之间的连接。

如果你占双NAT的话,那将会很好。

注意:

您可以使用STUN发现IP和端口,但是您必须编写自己的代码,通过保keepalive技术将IP:端口发送到您的服务器。

一旦一个客户端通过服务器上的唯一ID识别另一个客户端,它将与另一个客户端的IP一起提供:端口信息到UDP孔打出它需要发送和接收的数据。

小更新:

有一个图书馆是显示在地平线上的Java检查出来:
https://github.com/htwg/UCE#readme

这个例子是在C#中,而不是在Java中,但是NAT遍历的概念是语言不可知的。

请参阅Michael Lidgren的内置NAT穿越的networking库。

链接: http : //code.google.com/p/lidgren-network-gen3/特定的C#文件处理NAT遍历: http : //code.google.com/p/lidgren-network-gen3/source/browse/躯干/ Lidgren.Network / NetNatIntroduction.cs

您发布的过程是正确的。 它将工作,对于四种常用types的NAT设备 (我说一般,因为NAT行为没有真正标准化)中只有三种:全锥形NAT,受限锥形NAT和端口受限锥形NAT。 对于大多数在企业networking中find的增强安全性的对称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

具有唯一命名大约43亿台计算机的IPv4地址已经用完了。 聪明的人预见到了这个问题,并且除其他原因之外,通过分配一个连接到自己的共享IP地址的计算机的networking,发明了防止IPv4地址耗尽的路由器。

有局域网IP。 然后有WAN IP。 局域网IP是局域网IP,用于唯一标识本地networking中的计算机,例如连接到家庭路由器的台式机,便携式计算机,打印机和智能手机。 广域网IP在广域网中唯一标识局域网之外的计算机 – 通常被认为是互联网。 所以这些路由器为一组计算机分配一个WAN IP。 每台电脑还有自己的局域网IP。 LAN IP是您在命令提示符下键入ipconfig并获取IPv4 Address . . . . . . . . 192.168.1.101看到的内容IPv4 Address . . . . . . . . 192.168.1.101 IPv4 Address . . . . . . . . 192.168.1.101 IPv4 Address . . . . . . . . 192.168.1.101 。 当您连接到cmyip.com并获得128.120.196.204时,WAN IP就是您所看到的。

正如无线电频谱被收购一样 ,整个IP范围也被代理机构和组织以及端口号购买和保留。 简短的信息是,我们再也没有更多的IPv4地址了。

这与NAT穿越有什么关系? 那么,由于路由器的发明,直接连接( 端到端的连接 )已经有些…不可能,没有less数黑客。 如果您有两台计算机(计算机A和计算机B)共享广域网IP为128.120.196.204计算机连接到哪个networking? 我正在谈论一个外部计算机(比如说google.com) 启动一个连接到128.120.196.204 。 答案是: 没有人知道 ,路由器也没有,这就是路由器断开连接的原因。 如果计算机A 启动google.com的连接,那么情况google.com一样了。 路由器然后记得计算机A与局域网IP 192.168.1.101通知74.125.227.64(google.com)的连接。 当计算机A的请求数据包离开路由器时,路由器实际上 LAN IP 192.168.1.101 重写128.120.196.204的路由器WAN IP。 因此,当google.com收到计算机A的请求数据包时,它会看到路由器重新写入的发件人IP,而不是计算机A的LAN IP(google.com将128.120.196.204视为要回复的IP)。 当google.com最终回复时,数据包到达路由器,路由器记得 (它有一个状态表),它正在等待google.com的回复,并将数据包正确地转发给计算机A.

换句话说,当启动连接时,你的路由器没有任何问题 – 你的路由器会记得将回复的数据包转发回你的计算机(通过上面描述的整个过程)。 但是,当外部服务器启动到您的连接,路由器不能知道连接是128.120.196.204计算机,因为计算机A和计算机B都共享广域网IP为128.120.196.204 …除非有明确的规则它指示路由器转发所有原先去往目的地端口X数据包,现在转到计算机A,目的地端口Y 这就是所谓的端口转发 。 不幸的是,如果你正在考虑为你的networking应用程序使用端口转发,这是不实际的,因为你的用户可能不知道如何启用它,并且可能不愿意启用它,如果他们认为这是一个安全风险。 UPnP简单地指的是允许您以编程方式启用端口转发的技术 。 不幸的是,如果您正在考虑使用UPnP来端口您的networking应用程序,那么这也不实际,因为UPnP并不总是可用,并且当它不可用时,它可能无法默认打开。

那么,解决scheme是什么? 解决scheme是通过自己的计算机(您已经仔细预先configuration为可全局访问)代理您的整个stream量,或想出一个方法来击败系统。 第一个解决scheme是(我相信)被称为TURN ,并以提供可用带宽的服务器场为代价,神奇地解决了所有连接问题。 第二个解决scheme称为NAT穿越,这是我们接下来将要探索的。

之前,我描述了一个外部服务器(例如google.com)启动到128.120.196.204的连接的128.120.196.204 。 我说过,如果路由器没有明确的规则去理解哪台电脑转发谷歌的连接请求,那么路由器就会简单地删除连接。 这是一个普遍的情况,并且不准确,因为有不同types的NAT。 (注意:路由器是可以放在地板上的实际物理设备,NAT(networking地址转换)是一个编程到路由器中的软件过程,它有助于节省树木等IPv4地址)。 所以,根据路由器使用的NAT,连接情况会有所不同。 路由器甚至可能结合 NAT进程。

有四种具有标准化行为的NAT:全圆锥NAT,受限圆锥NAT,端口受限圆锥NAT和对称NAT。 除了这些types之外,还有其他types的非规范化的NAT,但更为罕见。

注意:我对NAT并不是很熟悉,似乎有很多方法可以查看路由器,因此互联网上的信息非常分散。 维基百科说,对全面的,受限制的和限制端口的锥体进行的NAT分类已经有所废弃了。 有一种叫做静态和dynamicNAT的东西,只是一些我无法调和在一起的各种概念。 不过,下面的模型适用于我自己的应用程序。 你可以通过阅读下面和上面的链接以及整个这篇文章来了解更多关于NAT的内容。 我不能多发表一些关于他们的事情,因为我不太了解他们。

希望一些networking专家能够纠正/添加input,这样我们都可以更多地了解这个神秘的过程。

要回答有关收集每个客户端的外部IP和端口的问题:

所有UDP数据包的头部结构相同 , 只有一个源IP和一个源端口。 UDP数据包标头不包含“内部”源IP和“外部”源IP。 UDP数据包头只包含一个源IP。 如果你想获得“内部”和“外部”源IP,你需要实际发送内部源IP作为你的有效载荷的一部分。 但是这听起来不像你需要一个内部源IP和端口。 这听起来像你只需要一个外部的IP和端口,如你的问题所述。 这意味着你的解决scheme就是简单地从数据包中读取源IP和端口,就像字段一样。

以下两种情况(他们没有真正解释别的):

局域网通信

计算机A的局域网IP为192.168.1.101。 计算机B的局域网IP为192.168.1.102。 计算机A从端口3000发送一个数据包到端口6000的计算机B. UDP数据包的源IP将是192.168.1.101。 这将是唯一的知识产权。 “外部”在这里没有语境,因为networking纯粹是一个局域网。 在这个例子中,广域网(如Internet)不存在。 关于端口,因为我不确定NAT,所以我不确定在数据包上写入的端口是否是3000. NAT设备可能会将数据包的端口从3000重新写入像49826那样的随机数。无论哪种方式,应该使用封装上的任何端口来回复 – 这就是你应该用来回复。 所以在这个局域网通信的例子中,你只需要发送一个IP – 局域网IP,因为这是最重要的。 您不必担心端口 – 路由器为您处理。 当您收到数据包时,您只需从数据包中读取数据,即可收集唯一的IP和端口。

广域网通信

计算机A的局域网IP也是192.168.1.101。 计算机B有一个局域网IP,同样是192.168.1.102。 计算机A和计算机B将共享128.120.196.204的WAN IP。 服务器S是一个服务器,一个全球可到达的计算机,假设一台Amazon EC2服务器,其WAN IP为1.1.1.1。 服务器S可能有一个局域网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.尽pipe这些不是用于发送数据包的原始IP和端口计算机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()getPort()可以在客户端(发送)机器上返回目标端口或源端口,具体取决于您设置的目的端口还是源端口,请将setAddress()setPort()调用到服务器(接收)并在服务器(接收)机器上,将setAddress()setPort()返回给客户机(发送)机器。 receive()必须有一个方法来做到这一点。 请详细说明( getAddress()getPort()是否不返回您期望的源IP和端口)是否是您的实际障碍。 这是假设服务器是一个“标准的”UDP服务器(它不是一个STUN服务器)。

进一步更新:

我阅读了关于“ 如何使用STUN从一个客户端获取IP和端口并将其交给另一个客户端 ”的更新? STUN服务器不用于交换端点或执行NAT遍历。 STUN服务器旨在告诉你你的公共IP,公共端口和NAT设备的types(无论是全锥形NAT,受限制锥形NAT,还是端口受限锥形NAT)。 我会呼叫中间人服务器负责交换端点,并执行实际的NAT穿越“介绍人”。 在我的个人项目中 ,我并不需要使用STUN来执行NAT遍历。 我的“介绍者”(介绍客户端A和B的中间人服务器)是侦听UDP数据报的标准服务器。 当客户A和B都向介绍人注册时,介绍人读取他们的公共IP和端口以及私人IP(以防他们在局域网中)。 公共IP从数据报首部读出,就像所有的标准UDP数据报一样。 私有IP被写为数据报有效载荷的一部分,导入者只是将其作为有效载荷的一部分读取。 因此,关于STUN的用处,您不需要依靠STUN来获取每个客户端的公共IP和公共端口,任何连接的套接字都可以告诉您这一点。 我会说STUN仅用于确定您的客户端在哪种types的NAT设备,以便您知道是否执行NAT穿越(如果NAT设备types是全圆锥体,受限制或端口限制),或执行全部的TURNstream量代理(如果NAT设备types是对称的)。

请详细说明您的障碍:如果您需要关于devise应用程序消息传递协议的最佳实践方面的build议,以及关于以有序和系统的方式阅读收到消息的领域的build议(根据您在下面发表的评论),可否分享您的最新消息方法?

你的问题是非常广泛的 – 我不能提供一个例子,但下面的链接可能会有所帮助(规格,库,样品等):

STUN的工作原理如下:防火墙后的客户端连接到防火墙外的STUN服务器。 STUN服务器检查从客户端收到的数据包,并向客户端发送一个响应,其中包含客户端IP和端口,因为它们出现在STUN服务器上。

这就是防火墙后面的客户端如何发现自己的外部IP和端口。 据我所知,STUN服务器通常不会将地址信息从一个客户端传递到另一个客户端。

通常,STUN用于通过防火墙设置媒体stream,当防火墙已经对信令stream量开放时(例如,在VoIP中):客户端联系STUN服务器以发现其自己的用于UDPstream量的外部IP和端口,然后将其信令请求SIP INVITE或其他)到另一个客户端上的一个众所周知的开放端口 – 包括在有效负载(SDP或其他)中的外部UDP地址信息。 所以通常一个客户端需要通过一个开放的端口到达信号才能进行点对点通信。

你的问题不是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)所做的工作,以真正理解这个问题。 维基百科的锥体分类是完全过时和误导的。

Interesting Posts