Java UDP STUN 打孔与 DatagramSocket

Posted

技术标签:

【中文标题】Java UDP STUN 打孔与 DatagramSocket【英文标题】:Java UDP STUN Hole Punching with DatagramSocket 【发布时间】:2012-04-11 23:56:48 【问题描述】:

我正在尝试通过 NAT 向客户端发送 udp 数据包,我们都属于不同的 NAT,我们熟悉 STUN 的理论,因此实现此目的的方法是“打孔”我们的通过一个简单的 STUN 服务器..

基本上,服务器只返回“已连接”的另一个客户端的外部 IP 地址和端口,然后我可以使用它通过 NAT 将数据包发送到客户端......但是尽管我们设法获取了彼此的外部 ip和端口..发送后我们仍然无法收到来自对方的任何东西...在搜索论坛和数小时的头颅后,我们仍然无法提出解决方案...想知道是否有人熟悉STUN 能够给我们一些关于我们哪里出错的指示或建议......

下面是我们写的小客户端...

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.SocketException;
import java.net.UnknownHostException;

import javax.swing.JOptionPane;


public class Client 

DatagramSocket socket;

public Client()
    try 
        socket = new DatagramSocket();
        String data = "Initiate Stun Server";
        byte[] receive = data.getBytes();

        InetAddress host = InetAddress.getByName("the.stun.server.ipaddress");
        DatagramPacket pk = new DatagramPacket(receive,receive.length,host,9345);
        socket.send(pk); //send packet to server to initiate udp communication

        //spawn a new Thread to listen for any incoming packets
        new Thread()
            public void run()
                byte[] r;
                DatagramPacket rp;
                while(true)
                    System.out.println("Start listening on new socket");
                    r = new byte[1024];
                    rp = new DatagramPacket(r,r.length);
                    try 
                        socket.receive(rp);
                        System.out.println(new String(rp.getData()));
                     catch (IOException e) 
                        e.printStackTrace();
                    
                
            
        .start();

        String m = JOptionPane.showInputDialog(null,"Enter message to send");
        InetAddress connect = InetAddress.getByName(JOptionPane.showInputDialog(null,"Enter address to send message to"));//This is where we input the external ip
        int connectPort = Integer.parseInt(JOptionPane.showInputDialog(null,"Enter port of the addressee"));//input port
        DatagramPacket p = new DatagramPacket(m.getBytes(),m.getBytes().length,connect,connectPort);
        socket.send(p);

     catch (SocketException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
     catch (UnknownHostException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
     catch (IOException e) 
        // TODO Auto-generated catch block
        e.printStackTrace();
    


public static void main(String args[])
    Client c = new Client();



【问题讨论】:

【参考方案1】:

您实现的不是真正的 STUN 协议,但可能就足够了。 :)

但我认为我在您的代码中发现了两个问题。

    您没有保存本地端口号。从 stun 服务器返回响应后,您需要调用 socket.getLocalPort 以找出与“映射端口”相对应的内部端口号。映射端口是您的 stun 服务器看到您的端口。您的 NAT 将继续将来自您 PC 的 IP 的出站流量映射到该映射端口,但前提是您使用相同的本地端口。因此,在您与对等方的后续连接中,在同一端口上创建数据报套接字(在关闭原始套接字之后),或者只是重用同一个套接字来与对等方进行后续通信,因为套接字已经绑定。

    仅仅因为你知道远程主机的外部 IP 地址和他的本地套接字的端口映射,并不意味着他的 NAT 会转发你的数据包。大多数 NAT 以“IP 和端口受限”的方式运行。这意味着,如果它知道同一远程主机的 IP 和端口有相应的出站 UDP 数据包,它将只允许通过 NAT 的入站流量,包括 UDP 数据包。如果它没有这个规则,它就不会知道将数据包转发到 NAT 后面的哪台 PC。典型的 NAT 穿越技术是让双方同时向对方发送简单的 1 字节数据报,并反复尝试(不止一次)。第一个数据包试图离开主机并离开它自己的 NAT,但很可能会被远程 NAT 阻止(因为它不知道你是谁)。但它确实会导致您的 NAT 创建一个映射和转发条目,以便对方成功发送给您。最终,两个 NAT 都将允许并转发两个对等方之间的流量。

还有一些类型的 NAT 具有不可预测的端口映射行为。 (端口映射按 IP 更改)。这些很难遍历(使用 STUN),但如果对方有一个表现良好的 NAT,通常可以正常工作。幸运的是,这些类型的 NAT 比以前少了。

这里有一些链接:

ICE(通过使用 STUN 和 TURN 实现的 P2P 标准机制):http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment

我的P2P connectivity in a nutshell answer 我给了一段时间。

另外,一个公然使用my STUN server code base 的插件。您可以将它与JStun 客户端库结合使用。

【讨论】:

以上是关于Java UDP STUN 打孔与 DatagramSocket的主要内容,如果未能解决你的问题,请参考以下文章

使用 STUN 打孔

无法使用 STUN 执行 TCP 打孔

用于 UDP NAT 打孔的 PHP 和 Java...?

UDP打孔混乱

UDP打孔到期[关闭]

Java TCP 打孔