我的上一篇日志是说明打洞原理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发送即可。
以上推论需要环境测试,欢迎大家测试告之结果。