Java UDP打孔示例 - 通过防火墙连接 [英] Java UDP hole punching example - connecting through firewall

查看:234
本文介绍了Java UDP打孔示例 - 通过防火墙连接的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我有两台电脑。

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

They know each others public and private IPs via ice4j.

一个客户端正在侦听,另一个发送一些字符串。

One client listening and the other one sending some string.

我希望通过UPD打孔来实现这一点:

I'd like to see this happen via UPD hole punching:

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有助于猜测端口号并在客户端A和B之间建立连接。



如果你把双NAT算作为好。

Could any one post pseudo examples of how to go about doing hole punching through symmetric NAT? Assuming there will be server S that will help to guess the port numbers and establish connection between the client A and B.

It would be nice if you accounted for double NAT as well.

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

You can use STUN to discover the IP and Port but you have to write your own code that would send the IP:Port to your server via keepalive technique.

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

Once one client identifies the other via unique ID on the server it will be provided with the other's client IP:port info to UDP hole punch the data it needs to send and receive.

有一些库出现在java的地平线上检查出来:

https://github.com/htwg/UCE#readme

There is library that is showing up on the horizon for java check it out:
https://github.com/htwg/UCE#readme

推荐答案

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

This example is in C#, not in Java, but the concepts of NAT traversal are language-agnostic.

请参阅Michael Lidgren的内置NAT遍历的网络库。

See Michael Lidgren's network library which has NAT traversal built in.

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

您发布的过程是正确的。它只适用于4种常见类型的 NAT设备(我说一般因为NAT行为未真正标准化):全锥形NAT,受限锥形NAT和端口限制锥形NAT。 NAT遍历不适用于Symmetric NAT,后者主要位于企业网络中以增强安全性。如果一方使用对称NAT而另一方不使用,则仍然可以遍历NAT,但需要更多猜测。对称NAT遍历的对称NAT非常困难 - 你可以在这里阅读一篇关于它的论文

The process you've posted is correct. It will work, for only 3 out of 4 general types of NAT devices (I say general because NAT behavior isn't really standardized): Full-Cone NATs, Restricted-Cone NATs, and Port-Restricted-Cone NATs. NAT traversal will not work with Symmetric NATs, which are found mostly in corporate networks for enhanced security. If one party uses a Symmetric NAT and the other party doesn't, it's still possible to traverse the NAT but it requires more guesswork. A Symmetric NAT to Symmetric NAT traversal is extremely difficult - you can read a paper about it here.

但实际上,你所描述的过程完全正常。我已经为我自己的远程屏幕共享程序(不幸的是在C#中)实现了它。只需确保已禁用Windows防火墙(如果您使用的是Windows)和第三方防火墙。但是,我很高兴地确认它会起作用。

But really, the process you've described works exactly. I've implemented it for my own remote screen sharing program (also in C#, unfortunately). Just make sure you've disabled Windows firewall (if you're using Windows) and third-party firewalls. But yes, I can happily confirm that it will work.

澄清NAT遍历的过程

我正在编写此更新,以便为您和未来的读者阐明NAT遍历的过程。希望这可以是对历史和过程的清晰总结。

I'm writing this update to clarify the process of NAT traversal for you and future readers. Hopefully, this can be a clear summary of the history and the process.

一些参考资料来源: 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

Some Reference Sources: http://think-like-a-computer.com/2011/09/16/types-of-nat/, and 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地址耗尽。

IPv4 addresses, with the capacity to uniquely name approximately 4.3 billion computers, have run out. Smart people foresaw this problem, and, among other reasons, invented routers to combat IPv4 address exhaustion, by assigning a network of computers connected to itself 1 shared IP address.

那里是局域网IP。然后是WAN IP。 LAN IP是局域网IP,可以唯一地识别本地网络中的计算机,例如连接到家庭路由器的台式机,笔记本电脑,打印机和智能手机。 WAN IP在广域网中唯一地识别局域网外的计算机 - 通常用于表示Internet。因此,这些路由器为一组计算机分配1个WAN IP。每台计算机仍然有自己的LAN IP。当您在命令提示符中键入 ipconfig 并获取 IPv4地址时,您会看到LAN IP。 。 。 。 。 。 。 。 192.168.1.101 。当您连接到 cmyip.com 并获得 128.120.196.204 时,您会看到WAN IP。

There are LAN IPs. And then there are WAN IPs. LAN IPs are Local Area Network IPs which uniquely identify computers in a local network, say the desktops, laptops, printers, and smartphones connected to a home router. WAN IPs uniquely identify computers outside of the local area network in a wide area network - commonly taken to mean The Internet. So these routers assign a group of computers 1 WAN IP. Each computer still has its own LAN IP. LAN IPs are what you see when you type ipconfig in your Command Prompt and get IPv4 Address . . . . . . . . 192.168.1.101. WAN IPs are what you see when you connect to cmyip.com and get 128.120.196.204.

正如无线电频谱一样已被买断,因此整个IP范围都被代理商和组织买断并保留,以及端口号。短信再次表明我们没有更多的IPv4地址可供使用。

Just as the radio spectrum is bought out, so entire IP ranges are bought out and reserved as well by agencies and organizations, as well as port numbers. The short message is, again, that we don't have any more IPv4 addresses to spare.

这与NAT遍历有什么关系?好吧,既然发明了路由器,直接连接(端到端连接)有点......不可能,没有一些黑客。如果您有一台2台计算机(计算机A和计算机B)的网络,它们共享 128.120.196.204 的WAN IP,连接到哪台计算机?我在谈论外部计算机(比如google.com)发起 128.120.196.204 的连接。答案是:没人知道,路由器也没有,这就是路由器断开连接的原因。如果计算机A 启动 google.com 的连接,那么这是一个不同的故事。然后路由器记住,计算机A的局域网IP 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 。

What does this have to do with NAT traversal? Well, since routers were invented, direct connections (end-to-end connectivity) have been somewhat ... impossible, without a few hacks. If you have a network of 2 computers (Computer A and Computer B) both sharing the WAN IP of 128.120.196.204, to which computer does a connection go? I'm talking about an external computer (say google.com) initiating a connection to 128.120.196.204. The answer is: nobody knows, and neither does the router, which is why the router drops the connection. If Computer A initiates a connection to, say, google.com, then that's a different story. The router then remembers that Computer A with LAN IP 192.168.1.101 intiated a connection to 74.125.227.64 (google.com). As Computer A's request packet leaves the router, the router actually re-writes LAN IP 192.168.1.101 to the router's WAN IP of 128.120.196.204. So, when google.com receives Computer A's request packet, it sees the sender IP that the router re-wrote, not the LAN IP of Computer A (google.com sees 128.120.196.204 as the IP to reply to). When google.com finally replies, the packet reaches the router, the router remembers (it has a state table) that it was expecting a reply from google.com, and it appropriately forwards the packet to Computer A.

换句话说,当发起连接时你的路由器没有问题 - 你的路由器会记得把回复数据包转发给你的计算机(通过上述整个过程)。但是,当外部服务器启动连接时,路由器无法知道连接所针对的计算机,因为计算机A和计算机B都共享 128.120.196.204 ...除非,有一个明确的规则指示路由器转发最初转到目的端口 X 的所有数据包,现在转到转到计算机A,目标端口 Y 。这称为端口转发。不幸的是,如果您正在考虑为您的网络应用程序使用端口转发,那么这是不切实际的,因为您的用户可能不了解如何启用它,并且如果他们认为存在安全风险,可能不愿意启用它。 UPnP 只是指允许您以编程方式启用端口转发的技术。不幸的是,如果您正在考虑使用UPnP来移植您的网络应用程序,那么它也不实用,因为UPnP并不总是可用,并且当它出现时,默认情况下它可能无法启用。

In other words, your router has no problem when you initiate the connection - your router will remember to forward the replying packet back to your computer (through that whole process described above). But, when an external server initiates a connection to you, the router can't know which computer the connection was meant for, since Computer A and Computer B both share the WAN IP of 128.120.196.204 ... unless, there's a clear rule that instructs the router to forward all packets originally going to destination port X, now to go to Computer A, destination port Y. This is known as port-forwarding. Unfortunately, if you're thinking of using port-forwarding for your networking applications, it's not practical, as your users may not understand how to enable it, and may be reluctant to enable it if they think it's a security risk. UPnP simply refers to the technology that allows you to programatically enable port-forwarding. Unfortunately, if you're thinking of using UPnP to port-forward your networking applications, it's not practical either, as UPnP is not always available, and when it is, it may not turned on by default.

那么解决方案是什么呢?解决方案是通过您自己的计算机(您已经仔细预先配置为可全局访问)代理整个流量,或者想出一种方法来击败系统。第一个解决方案(我相信)称为 TURN ,并以神奇的价格神奇地解决所有连接问题提供具有可用带宽的服务器群。第二个解决方案称为NAT遍历,这是我们接下来要探索的内容。

So what's the solution then? The solution is to either proxy your entire traffic over your own computer (which you have carefully pre-configured to be globally reachable), or to come up with a way to beat the system. The first solution is (I believe) called TURN, and magically solves all connectivity issues at the price of providing a farm of servers with the available bandwidth. The second solution is called NAT traversal, and it's what we'll be exploring next.

之前,我描述了外部服务器(比如google.com)启动的过程与 128.120.196.204 的连接。我说过,如果路由器没有特定的规则来了解哪台计算机转发谷歌的连接请求,路由器就会放弃连接。这是一个广义的场景,并不准确,因为有不同类型的NAT。 (注意:路由器是您可以放在地板上的实际物理设备.NAT(网络地址转换)是一个编程到路由器中的软件进程,它有助于保存像树一样的IPv4地址。因此,根据路由器使用的 NAT,连接方案会有所不同。路由器甚至可以组合 NAT进程。

Earlier, I described the process of an external server (say google.com) initiating a connection to 128.120.196.204. I said that, without the router having specific rules to understand which computer to forward google's connection request to, the router would simply drop the connection. This was a generalized scenario, and is not accurate because there are different types of NATs. (Note: A router is the actual physical device that you can drop on the floor. NAT (Network Address Translation) is a software process programmed into the router which helps save IPv4 addresses like trees). So, depending on which NAT the router employs, connection scenarios vary. A router may even combine NAT processes.

有四种类型的具有标准化行为的NAT:全锥NAT,受限锥NAT, Port-Restricted-Cone NAT和对称NAT。除了这些类型之外,还可能存在其他类型的具有非标准化行为的NAT,但这种情况更为罕见。

There are four types of NATs with standardized behavior: Full-Cone NATs, Restricted-Cone NATs, Port-Restricted-Cone NATs, and Symmetric NATs. Aside from these types, there can be other types of NATs with non-standardized behavior, but it's rarer.

注意:我对NAT并不太熟悉......似乎有很多方法可以查看路由器,互联网上的信息非常分散关于这个话题。维基百科表示,对完整,限制和端口限制的视锥细胞进行NAT分类已经有所弃用?有一种叫静态和动态NAT的东西......只是一堆我无法调和的各种概念。尽管如此,以下模型适用于我自己的应用程序。您可以通过阅读以下链接以及整篇文章了解有关NAT的更多信息。我不能发布更多关于它们的信息,因为我对它们并不是很了解。

Note: I'm not really too familiar with NATs...it seems like there are many ways of looking at routers, and information on the internet is very spread out on this topic. Classifying NATs by full, restricted, and port-restricted cones has been somewhat deprecated, says Wikipedia? There's something called static and dynamic NATs...just a bunch of various concepts that I can't reconcile together. Nevertheless, the following model worked for my own application. You can find out more about NATs by reading the links below and above and throughout this post. I can't post more about them because I don't really understand much about them.

希望一些网络大师能够纠正/添加输入,这样我们就可以了了解有关此神秘过程的更多信息。

Hoping for some network gurus to correct/add input, so that we can all learn more about this mysterious process.

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

To answer your question about gathering the external IP and Port of each client:

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

The headers of all UDP packets are structured the same with one source IP and one source port. UDP packet headers do not contain an "internal" source IP and an "external" source IP. UDP packet headers only contain one source IP. If you want to get an "internal" and "external" source IP, you need to actually send the internal source IP as part of your payload. But it doesn't sound like you need an internal source IP and port. It sounds like you only need an external IP and port, as your question stated. Which means that your solution it to simply read the source IP and port off the packet like the fields they are.

下面的两个场景(它们没有真正解释其他任何事情):

Two scenarios below (they don't really explain anything else):

局域网通信

计算机A的局域网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和端口。

Computer A has a LAN IP of 192.168.1.101. Computer B has a LAN IP of 192.168.1.102. Computer A sends a packet, from port 3000, to Computer B at port 6000. The source IP on the UDP packet will be 192.168.1.101. And that will be the only IP. "External" has no context here, because the network is purely a local area network. In this example, a wide area network (like the Internet) doesn't exist. About ports though, because I'm unsure about NATs, I'm not sure if the port inscribed on the packet will be 3000. The NAT device may re-write the packet's port from 3000 to something random like 49826. Either way, you should use whatever port inscribed on the packet to reply - it's what you're supposed to use to reply. So in this example of LAN communication, you need send only one IP - the LAN IP, because that's all that matters. You don't have to worry about the port - the router takes care of that for you. When you receive the packet, you gather the only IP and port simply by reading it off the packet.

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也无关紧要。

Computer A has a LAN IP, again, of 192.168.1.101. Computer B has a LAN IP, again, of 192.168.1.102. Both Computer A and Computer B will share a WAN IP of 128.120.196.204. Server S is a server, a globally reachable computer on, let's say, an Amazon EC2 server, with a WAN IP of 1.1.1.1. Server S may have a LAN IP, but it's irrelevant. Computer B is irrelevant too.

计算机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,则需要从头部传输正常数据单独

Computer A sends a packet, from port 3000, to Server S. On the way out the router, the packet's source LAN IP from Computer A gets re-written to the WAN IP of the router. The router also re-writes the source port of 300 to 32981. What does Server S see, in terms of the external IP and port? Server S sees 128.120.196.204 as the IP, not 192.168.1.101, and Server S sees 32981 as the port, not 3000. Although these aren't the original IP and ports Computer A used to send the packet, these are the correct IPs and ports to reply to. When you receive the packet, you can only know the WAN IP and rewritten port. If that's what you want (you were asking for just the external IP and port), then you're set. Otherwise, if you also wanted the internal IP of the sender, you would need to have transmitted that as normal data separate from your header.

代码:

如上所述(回答您的问题关于收集外部IP),收集每个客户端的外部IP和端口,您只需从数据包中读取它们。发送总是的每个数据报都有发送者的源IP和源端口;你甚至不需要花哨的自定义协议,因为这两个字段总是包含在内 - 根据定义,每个UDP数据包必须有这两个字段。

As stated above (below To answer your question about gathering the external IP), to gather the External IP and Port of each client, you simply read them off the packet. Each datagram sent always has the source IP and source port of the sender; you don't even need a fancy custom protocol because these two fields are always included - every single UDP packet must, by definition, have these two fields.

// 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服务器)。

Because getAddress() and getPort() can return either the destination or source port, depending on what you set it to be, on the client (sending) machine, call setAddress() and setPort() to the server (receiving) machine, and on the server (receiving) machine, call setAddress() and setPort() back to the client (sending) machine. There must be a way to do this in receive(). Please elaborate if this (getAddress() and getPort() don't return the source IP and port you expect) is your actual roadblock. This is assuming the server to be a "standard" UDP server (it's not a STUN server).

进一步更新:

我阅读了有关如何使用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),或执行全部转出流量代理(如果NAT设备类型是对称的)。

I read your update about "how to use STUN to take the IP and port from one client and give it to the other"? A STUN server isn't designed to exchange endpoints or perform NAT traversal. A STUN server is designed to tell you your public IP, public port, and type of NAT device (whether it's a Full-Cone NAT, Restricted-Cone NAT, or Port-Restricted Cone NAT). I'd call the middleman server responsible for exchanging endpoints and performing the actual NAT traversal the "introducer". In my personal project, I don't actually need to use STUN to perform NAT traversing. My "introducer" (the middleman server that introduces clients A and B) is a standard server listening for UDP datagrams. As both clients A and B register themselves with the introducer, the introducer reads off their public IP and port and private IP (in case they're on a LAN). The public IP is read off the datagram header, like for all standard UDP datagrams. The private IP is written as part of the datagram payload, and the introducer just reads it as part of the payload. So, about STUN's usefulness, you don't need to rely on STUN to get the public IP and public port of each of your clients - any connected socket can tell you this. I'd say STUN is useful only for determining what type of NAT device your client is under so that you know whether to perform NAT traversal (if the NAT device type is Full-Cone, Restricted, or Port-Restricted), or to perform all-out TURN traffic proxying (if the NAT device type is Symmetric).

请详细说明您的障碍:如果您需要有关设计应用程序消息传递协议的最佳做法的建议,以及有序和系统地阅读收到消息的字段的建议(基于您在下面发布的评论),您能分享您当前的方法吗?

Please elaborate on your roadblock: if you want advice on best practices for designing an application messaging protocol, and advice on reading the fields off received messages in an orderly and systematic fashion (based on the comment you posted below), could you share your current method?

这篇关于Java UDP打孔示例 - 通过防火墙连接的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆