穿越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(); //构造客户端的 DatagramSocket
		register(); //向服务器发送字符串: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 socket
		new 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发送即可。

 

以上推论需要环境测试,欢迎大家测试告之结果。

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值