当前位置:编程学习 > JAVA >>

java udp穿透NAT,怎么实现?

--------------------编程问答-------------------- 忒专业了吧,又是udp又是nat的

帮你顶 --------------------编程问答-------------------- 确实比较牛,呵呵,看了穿透似乎很高深的技术  --------------------编程问答-------------------- 用UDP穿透NAT可以解决P2P软件中的两个通过NAT上网的客户端直接通信的问题。
需要一个中介来帮助找到对方。

Java代码如下:

UDPAgent.java:

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.util.regex.Pattern;


public class UDPAgent implements Runnable {

public static void main(String[] args) throws Exception {
   new UDPAgent(-1).start();
}

DatagramSocket ds;

byte[] recbuf = new byte[1024];

DatagramPacket rec = new DatagramPacket(recbuf, recbuf.length);

static String ipPattern = "([0-9]{1,3}.){3}[0-9]{1,3}";

static String portPattern = "[0-9]{1,5}";

static Pattern sendPattern = Pattern.compile("send " + ipPattern + " "
    + portPattern + " .*");

int port;

public UDPAgent(int port) {
   this.port = port;

}

public void init() throws Exception {
   if (port < 1024 || port > 655535) {
    ds = new DatagramSocket();
   } else {
    ds = new DatagramSocket(port);
   }
}

public void start() throws Exception {
   println("start");
   println("LocalPort:" + port);
   init();
   new Thread(this).start();// recive thread
   receive();
}

public void receive() {
   for (;;) {
    try {
     ds.receive(rec);
     String msg = new String(rec.getData(), rec.getOffset(), rec
       .getLength());
     String line = rec.getSocketAddress() + ":" + msg;
     println(line);
     onReceive(rec);
    } catch (Exception e) {
     e.printStackTrace();
    }
   }
}

public void onReceive(DatagramPacket rec) {

}

public void doCommand(String cmd) throws Exception {
   // command:
   // 1. send xxx.xxx.xxx.xxx xxx *******************
   if (sendPattern.matcher(cmd).matches()) {
    doSend(cmd);
   }
}

public void doSend(String cmd) throws Exception {
   println("CMD: " + cmd);
   String[] s = cmd.split(" ", 4);
   int port = Integer.parseInt(s[2]);
   InetSocketAddress target = new InetSocketAddress(s[1], port);
   byte[] bs = s[3].getBytes();
   doSend(target, bs);
}

public void doSend(SocketAddress addr, byte[] data) throws Exception {
   DatagramPacket pack = new DatagramPacket(data, data.length, addr);
   ds.send(pack);
}

public void run() {
   BufferedReader reader = new BufferedReader(new InputStreamReader(
     System.in));
   try {
    String line = reader.readLine();
    while (!"exit".equals(line)) {
     doCommand(line);
     line = reader.readLine();
    }
    System.exit(0);
   } catch (Exception e) {
    e.printStackTrace();
   }
}

public void println(String s) {
   System.out.println(System.currentTimeMillis() + ":" + s);
}
}


UDPClient.java


import java.net.DatagramPacket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.SocketAddress;

public class UDPClient extends UDPAgent {

/**
* @param args
*/
public static void main(String[] args) throws Exception {
   new UDPClient("www.javadoc.cn", 2008, -1).start();
}

String serverName;

int serverPort;

SocketAddress server;

public UDPClient(String host, int port, int localPort) {
   super(localPort);
   this.server = new InetSocketAddress(host, port);

}

public void start() throws Exception {
   println("start");
   init();
   register();
   new Thread(this).start();// recive thread
   receive();
}

public void onReceive(DatagramPacket rec) {
   try {
    report(rec);
    if (rec.getSocketAddress().equals(server)) {
     doCommand(new String(rec.getData(), rec.getOffset(), rec
       .getLength()));
    }
   } catch (Exception e) {
    e.printStackTrace();
   }

}

public void report(DatagramPacket rec) throws Exception {
   String s = rec.getSocketAddress()
     + new String(rec.getData(), rec.getOffset(), rec.getLength());
   byte[] buf = s.getBytes();
   ds.send(new DatagramPacket(buf, buf.length, server));
}

public void register() throws Exception {
   String msg = "register " + getLocalAddress() + " " + ds.getLocalPort();
   doSend(server, msg.getBytes());
}

public String getLocalAddress() throws Exception {
   InetAddress addr = InetAddress.getLocalHost();
   return addr.getHostAddress();
}
}

UDPServer.java



public class UDPServer extends UDPAgent {

public static void main(String[] args) throws Exception {
   new UDPServer(2008).start();
}

public UDPServer(int port) {
   super(port);
}
}

1。启动一个Server.

2。启动两个Client.

然后从Server端的Console里边可以看到两个Client的NAT后的地址和端口。

在Server段输入命令 send a.a.a.a A send b.b.b.b B hello

a.a.a.a是第一个Client的NAT后的ip,A端口号。

b是第二个。。。

输入这个命令后,A就会直接发给B一个 hello。 发送成功。 如果是同一个NAT后边,可能要让A发送到B的内网地址才能成功。 --------------------编程问答-------------------- http://jwzs.javaeye.com/blog/530981 --------------------编程问答-------------------- 咳咳。。不懂,帮顶。 --------------------编程问答--------------------  哎,不懂呢,帮顶,学习! --------------------编程问答-------------------- 以前自己翻译过的一篇文章 - http://home.jysq.net/space-14906-do-blog-id-13895.html --------------------编程问答-------------------- 嗯,源码是没有时间看到,原理是知道的。(
当一个计算机通过内网向外发送UDP包的时候,它所用的端口会被NAT服务器映射为一个NAT服务器自己的端口;而由于UDP是无连接的,服务器会将这个映射关系保留一小段时间,这时返回这个服务器端口的UDP包会自动转发到内网计算机上。
多数NAT服务器会加以限制,即必须内网主动外发过的目的地址,才可以回复UDP包。这样,如果两个机器C1、C2都在各自的NAT服务器N1、N2的后面,那么就复杂一点,需要一个中介,即两个NAT服务器之间的机器S。怎么通信呢?(以下的“包”都指UDP包)
C1以端口CP1向S发一个包,C2以CP2向S发一个包。两个包的端口分别被映射为NP1、NP2,S分别回复C1和C2,在包中告诉C1和C2对方的端口(NP2、NP1)。然后,C1以CP1向N2的NP2发包,C2以CP2向N1的NP1发包,要求通信。因为多数NAT会将来自同一个内网地址的同一个端口,映射为同样的端口,那么CP1仍被映射为NP1、CP2仍被映射为NP2,这样刚才C1向N2发送到包就会被N2认为是C2向N1发包的回复,N1也是这样,于是双方就可以这样通信了。当然,为了保证成功连接,这个连接请求可能需要持续多次,花费几秒钟。 --------------------编程问答-------------------- 高深,没接触过 --------------------编程问答-------------------- 谢谢各位朋友的回答,正在看,看明白后才知道怎么给分~~呵 --------------------编程问答-------------------- 看看了 --------------------编程问答--------------------
引用 8 楼 weihthchk 的回复:
嗯,源码是没有时间看到,原理是知道的。(
 当一个计算机通过内网向外发送UDP包的时候,它所用的端口会被NAT服务器映射为一个NAT服务器自己的端口;而由于UDP是无连接的,服务器会将这个映射关系保留一小段时间,这时返回这个服务器端口的UDP包会自动转发到内网计算机上。
 多数NAT服务器会加以限制,即必须内网主动外发过的目的地址,才可以回复UDP包。这样,如果两个机器C1、C2都在各自的NAT服务器N1、N2的后面,那么就复杂一点,需要一个中介,即两个NAT服务器之间的机器S。怎么通信呢?(以下的“包”都指UDP包)
 C1以端口CP1向S发一个包,C2以CP2向S发一个包。两个包的端口分别被映射为NP1、NP2,S分别回复C1和C2,在包中告诉C1和C2对方的端口(NP2、NP1)。然后,C1以CP1向N2的NP2发包,C2以CP2向N1的NP1发包,要求通信。因为多数NAT会将来自同一个内网地址的同一个端口,映射为同样的端口,那么CP1仍被映射为NP1、CP2仍被映射为NP2,这样刚才C1向N2发送到包就会被N2认为是C2向N1发包的回复,N1也是这样,于是双方就可以这样通信了。当然,为了保证成功连接,这个连接请求可能需要持续多次,花费几秒钟。


NAT映射表是建立在路由器上面的.现在大多数路由都是支持NAT缓存的.
中介要有一个公网的ip,两个c1和c2都给server-mid发送udp,就在两个custom的路由器上面建立了NAT缓存.
剩下的就是swich了,将c1和c2像server-mid的udp切换为p2p的...
这个是关键key point.
so its the theory..... --------------------编程问答--------------------
引用 8 楼 weihthchk 的回复:
嗯,源码是没有时间看到,原理是知道的。(
当一个计算机通过内网向外发送UDP包的时候,它所用的端口会被NAT服务器映射为一个NAT服务器自己的端口;而由于UDP是无连接的,服务器会将这个映射关系保留一小段时间,这时返回这个服务器端口的UDP包会自动转发到内网计算机上。
多数NAT服务器会加以限制,即必须内网主动外发过的目的地址,才可以回复UDP包。这样,如果两个机器C1、C2都在各自的NAT服务器N1、N2的后面,那么就复杂一点,需要一个中介,即两个NAT服务器之间的机器S。怎么通信呢?(以下的“包”都指UDP包)
C1以端口CP1向S发一个包,C2以CP2向S发一个包。两个包的端口分别被映射为NP1、NP2,S分别回复C1和C2,在包中告诉C1和C2对方的端口(NP2、NP1)。然后,C1以CP1向N2的NP2发包,C2以CP2向N1的NP1发包,要求通信。因为多数NAT会将来自同一个内网地址的同一个端口,映射为同样的端口,那么CP1仍被映射为NP1、CP2仍被映射为NP2,这样刚才C1向N2发送到包就会被N2认为是C2向N1发包的回复,N1也是这样,于是双方就可以这样通信了。当然,为了保证成功连接,这个连接请求可能需要持续多次,花费几秒钟。


学习了。。。。 --------------------编程问答-------------------- Mark,领教了,O(∩_∩)O~ --------------------编程问答-------------------- 我用java打洞技术。。。。 我自己能接受对方的,但对方受不了我的怎么回事? 谢谢  --------------------编程问答-------------------- 果然很高端的东西
补充:Java ,  Java SE
CopyRight © 2012 站长网 编程知识问答 www.zzzyk.com All Rights Reserved
部份技术文章来自网络,