UDP打洞的过程大致如此:
1、双方都通过UDP与服务器通讯后,网关默认就是做了一个外网IP和端口号 与你内网IP与端口号的映射,这个无需设置的,服务器也不需要知道客户的真正内网IP
2、用户A先通过服务器知道用户B的外网地址与端口
3、用户A向用户B的外网地址与端口发送消息,
4、在这一次发送中,用户B的网关会拒收这条消息,因为它的映射中并没有这条规则。
5、但是用户A的网关就会增加了一条允许规则,允许接收从B发送过来的消息
6、服务器要求用户B发送一个消息到用户A的外网IP与端口号
7、用户B发送一条消息,这时用户A就可以接收到B的消息,而且网关B也增加了允许规则
8、之后,由于网关A与网关B都增加了允许规则,所以A与B都可以向对方的外网IP和端口号发送消息。
具体代码:
Server:
package ljxwtl.udp;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
public class Server {
public static void main(String[] args) throws Exception {
DatagramSocket datagramSocket = new DatagramSocket(8080);
byte [] buffer = new byte[10240];
String remoteA = null;
String remoteB = null;
int count = 0;
while (true){
DatagramPacket datagramPacket = new DatagramPacket(buffer,0,buffer.length);
datagramSocket.receive(datagramPacket);
System.out.println("连接成功:"+ datagramPacket.getAddress().getHostAddress() + ":" + datagramPacket.getPort());
String message = new String(datagramPacket.getData(), 0, datagramPacket.getLength(), StandardCharsets.UTF_8);
if ("A".equalsIgnoreCase(message)){
remoteA = datagramPacket.getAddress().getHostAddress() + ":" + datagramPacket.getPort();
count ++;
}
else if ("B".equalsIgnoreCase(message)){
remoteB = datagramPacket.getAddress().getHostAddress() + ":" + datagramPacket.getPort();
count ++;
}
if (count == 2){
String[] splitA = remoteA.split(":");
DatagramPacket datagramPacketA = new DatagramPacket(remoteB.getBytes(StandardCharsets.UTF_8),0,remoteB.length(),InetAddress.getByName(splitA[0]),Integer.parseInt(splitA[1]));
datagramSocket.send(datagramPacketA);
String[] splitB = remoteB.split(":");
DatagramPacket datagramPacketB = new DatagramPacket(remoteA.getBytes(StandardCharsets.UTF_8),0,remoteA.length(),InetAddress.getByName(splitB[0]),Integer.parseInt(splitB[1]));
datagramSocket.send(datagramPacketB);
}
}
}
}
Client:
package ljxwtl.udp;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.FutureTask;
public class Client {
public static void main(String[] args) throws Exception {
DatagramSocket datagramSocket = new DatagramSocket();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(System.in));
System.out.println("输入客户端识别码:");
String clientType = bufferedReader.readLine();
DatagramPacket datagramPacket = new DatagramPacket(clientType.getBytes(StandardCharsets.UTF_8), 0, clientType.getBytes(StandardCharsets.UTF_8).length, InetAddress.getByName("ljxwtl.cn"), 8080);
datagramSocket.send(datagramPacket);
DatagramPacket datagramPacketReceive = new DatagramPacket(new byte[10240],0,10240,InetAddress.getByName("ljxwtl.cn"), 8080);
datagramSocket.receive(datagramPacketReceive);
String remoteIPAddresss = new String(datagramPacketReceive.getData(), 0, datagramPacketReceive.getLength());
System.out.println(remoteIPAddresss);
String[] splitIpPort = remoteIPAddresss.split(":");
//发送消息线程
new Thread(new FutureTask(()->{
while (true){
System.out.println("请输入内容:");
String readLine = bufferedReader.readLine();
DatagramPacket datagramPacketP2P = new DatagramPacket(readLine.getBytes(StandardCharsets.UTF_8),readLine.getBytes(StandardCharsets.UTF_8).length,InetAddress.getByName(splitIpPort[0]),Integer.parseInt(splitIpPort[1]));
datagramSocket.send(datagramPacketP2P);
}
})).start();
//接收消息线程
new Thread(new FutureTask(()->{
while (true){
DatagramPacket datagramPacketP2PReceive = new DatagramPacket(new byte[10240],0,10240,InetAddress.getByName(splitIpPort[0]),Integer.parseInt(splitIpPort[1]));
datagramSocket.receive(datagramPacketP2PReceive);
System.out.println(new String(datagramPacketP2PReceive.getData(),0,datagramPacketP2PReceive.getLength(), StandardCharsets.UTF_8));
}
})).start();
}
}



