首页 文章

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

提问于
浏览
36

可以说我有两台电脑 .

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

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

我想通过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有助于猜测端口号并在客户端A和B之间 Build 连接 .

如果你考虑到双NAT也会很好 .

注意:

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

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

小更新:

有一个库出现在地平线上为java检查出来:
https://github.com/htwg/UCE#readme

4 回答

  • 6

    此示例位于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/trunk/Lidgren.Network/NetNatIntroduction.cs

    您发布的流程是正确的 . 它会工作,对于只有3个4种一般类型的NAT devices的(我说一般,因为NAT行为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,它仍然可以穿越NAT,但它需要更多的猜测对称NAT到对称NAT穿越是非常困难的 - you can read a paper about it here .

    但实际上,你为my own remote screen sharing program实现了它的过程(不幸的是在C#中) . 只需确保您使用Windows)和第三方防火墙 . 但是,是的,我很高兴地确认它会起作用 .

    Clarifying the Process of NAT Traversal

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

    一些参考源:http://think-like-a-computer.com/2011/09/16/types-of-nat/http://en.wikipedia.org/wiki/Network_address_translationhttp://en.wikipedia.org/wiki/IPv4http://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 并获取 IPv4 Address . . . . . . . . 192.168.1.101 时,您会看到LAN IP . WAN IP是连接到 cmyip.com 并获得 128.120.196.204 时看到的内容 .

    就像the radio spectrum is bought out一样,整个IP范围也被机构和组织买断并保留,as well as port numbers . 短消息再次表明我们没有更多的IPv4地址可供使用 .

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

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

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

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

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

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

    希望一些网络大师纠正/添加输入,这样我们都可以更多地了解这个神秘的过程 .

    To answer your question 关于收集每个客户端的外部IP和端口:

    The headers of all UDP packets are structured the same具有一个源IP和一个源端口 . UDP数据包标头不包含"internal"源IP和"external"源IP . UDP包头只包含一个源IP . 如果要获取"internal"和"external"源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 . "External"这里没有上下文,因为网络纯粹是局域网 . 在这个例子中,广域网(如互联网)不确定NAT,我不确定数据包上的端口是否为3000. NAT设备可能会重新写入数据包's port from 3000 to something random like 49826. Either way, you should use whatever port inscribed on the packet to reply - it' s你're supposed to use to reply. So in this example of LAN communication, you need send only one IP - the LAN IP, because that'一切都很重要 . 你不必担心关于端口 - 路由器为您处理 . 收到数据包后,只需从数据包中读取即可收集唯一的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发送数据包 . 在离开路由器的路上,数据包'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用于发送数据包的原始IP和端口计算机A,这些是要回复的正确IP和端口 . 收到数据包后,您只能知道WAN IP和重写端口 . 如果这就是你想要的(你只要求外部IP和端口),那么你就设置好了 . 否则,如果您还需要发件人的内部IP,则需要将其作为与标头分开的普通数据进行传输 .

    Code:

    如上所述(关于收集外部IP的 To answer your question 以下),要收集每个客户端的外部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() 不是STUN服务器) .

    Further Update:

    我阅读了有关“如何使用STUN从一个客户端获取IP和端口并将其提供给另一个"? 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 "介绍人”的更新 . 在my personal project,我不在局域网上 . 与所有标准UDP数据报一样,公共IP从数据报头读取 . 专用IP是作为数据报有效负载的一部分编写的,介绍人只是将其作为有效负载的一部分读取 . 所以,关于STUN 's usefulness, you don' t需要依靠STUN来获取每个客户端的公共IP和公共端口 - 任何连接的套接字都可以告诉你这一点 . 我说STUN仅用于确定客户端所使用的NAT设备类型,以便了解是否执行NAT遍历(如果NAT设备类型为Full-Cone,Restricted或Port-Restricted),或执行全部TURN流量代理(如果NAT设备类型是Symmetric) .

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

  • 51

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

  • 10

    STUN基本上如下工作:防火墙后面的客户端连接到防火墙外的STUN服务器 . STUN服务器检查从客户端接收的数据包,并向客户端发回包含客户端IP和端口的响应,因为它们看起来像是STUN服务器 .

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

    通常,当防火墙已经对信令流量开放时,STUN用于通过防火墙 Build 媒体流 - 例如在VoIP中:客户端联系STUN服务器以发现自己的外部IP和UDP流量端口,然后将其信令请求(SIP INVITE或其他)发送到众所周知的开放端口上的其他客户端 - 包括其外部UDP地址信息在有效载荷(SDP或其他)中 . 因此,通常需要通过开放端口可访问一个客户端以用于发送对等通信的信令 .

  • 6

    您的问题与Java无关 . 如果你知道如何打开UDP连接,这就足够了 . 阅读以下link的内容 . 不要害怕 Headers ,它也涵盖UDP . 其余的只是Java编码 .

    P.S. :在您的方案中,缺少一步 . A和B都必须与S打开连接,因为S需要告诉B A正试图到达它 . 如果B没有与S的开放连接,则A和B无法开始一起通信 .

    UPDATE

    Jason的回答包含有关NAT遍历的错误和疯狂猜测 . 人们应该阅读Saikat Guha (mpi-sws.org/~francis/imc05-tcpnat.pdf)所做的工作,以真正理解这件事 . 维基百科的锥形分类完全过时且具有误导性 .

相关问题