穿越net 打洞原理___java实现(原创)
我的上一篇日志是说明打洞原理http://smallbee.iteye.com/blog/1029835
?
下面来说说如何用java实现穿越。? 感谢(你是我的谁?? 83289331)提供代码
?
服务器端:
?
public class UDPServer extends UDPAgent {public static void main(String[] args) throws Exception {//开启2008服务器端口,接收客户端udp请求,同时也接收用户命令new UDPServer(2008).start();}public UDPServer(int port) {super(port);}}?
客户端:
import java.net.DatagramPacket;import java.net.InetAddress;import java.net.InetSocketAddress;import java.net.SocketAddress;public class UDPClient extends UDPAgent {public static void main(String[] args) throws Exception {new UDPClient("127.0.0.1", 2008, -1).start();}private 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(); //构造客户端的 DatagramSocketregister(); //向服务器发送字符串:register " + getLocalAddress() + " " + ds.getLocalPort()new Thread(this).start();//往自己输入的一个ip port发送东西 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();}}?
?
共有父类:
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);public static String ipPattern = "([0-9]{1,3}.){3}[0-9]{1,3}";public static String portPattern = "[0-9]{1,5}";public 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(); //构造服务器udp socketnew Thread(this).start();//往自己输入的一个ip port发送东西 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);}}?
?当服务器启动日志:
1304581809784:start
1304581809784:LocalPort:2008
启动一个客户端在客户端日志:
1304581880627:start
此时在服务器端日志:
1304581809784:start
1304581809784:LocalPort:2008
1304581880674:/127.0.0.1:2465:register 99.6.150.31 2465
再启动一个客户端日志:
1304581964923:start
此时服务器端日志:
1304581809784:start
1304581809784:LocalPort:2008
1304581880674:/127.0.0.1:2465:register 99.6.150.31 2465
1304581964939:/127.0.0.1:2469:register 99.6.150.31 2469
?
相当于两个客户端连接服务器的IP都为99.6.150.31 端口分别为 2465和2469
?
由于本人在自己机子上模拟多客户端,其实还是在一个局域网内。
?
然后控制台到客户端1(即端口为2465那个),输入 send 99.6.150.31 2469 我要连接2469
这个时候,在客户端2(2469端口)会看到如下:1304582321669:/99.6.150.31:2465:我要连接2469
如果按照穿越原理,这种情况应该是不可能的,因为在2465的网关没有记录客户端1的信息,应该会被抛弃。但是这个步骤必做不可,原因是这样做,能在客户端1的网关记录客户端2的信息,这样客户端2就可以向客户端1网关发起请求而不被抛弃(我是这么认为的)。
最后用客户端2发起一个请求给客户端1,这个时候,由于客户端1的网关已经有记录客户端2的信息,所以不会被抛弃,这样通信链路就连上了。
?
接下来,如果客户端1要往客户端2发送信息只要往 99.6.150.31 2469发送。客户端2往客户端1发送只要往99.6.150.31 2465发送即可。
?
以上推论需要环境测试,欢迎大家测试告之结果。
?
?
?
?
1 楼 Ralfc 2012-05-04 测试了这个程序,局域网内可以通过服务器端显示的端口号连接;外网的话只有服务器可以通过显示的端口号连接相应的客户端,而两台客户端之间无法通过IP与端口实现通信。 2 楼 Ralfc 2012-05-04 如果您有最研究的话,欢迎和我联系 r_evo@qq.com