Java 确定NAT类型和对称NAT上的端口预测
首先很抱歉我的英语不好,但它不是我的第一语言。 我的问题是:我正在使用Java实现一个简单的桌面共享软件。第一个问题是如何与NAT后面的两个客户端通信。通过对Internet的一些研究,我发现一个更好的解决方案是使用UDP打孔技术。 如果我理解正确,此函数的工作方式如下: A=客户1 NA=客户端1的nat B=客户2 NB=客户端2的nat S=公共服务器 1-A向S发送UDP数据包 2-S接收公共IP和NA的公共端口 3-B向S发送UDP数据包 4-S接收公共IP和NB的公共端口 5-S通过从a接收第一个数据包的绑定套接字向a发送一个UDP数据包,该数据包的公共ip和端口为B 6-S通过从B接收第一个数据包的绑定套接字,使用a的公共ip和端口向B发送UDP数据包 7-A向B发送一个UDP数据包,其中包含从S收到的详细信息,NB丢弃此数据包,因为它不知道谁是A(它没有此数据包的路由)。但这在NA中打开了一个孔,可以接受并插入从B到达的a包 8-B向a发送UDP数据包,NA可以解析该数据包 我知道这种方法只适用于4种NAT类型中的3种:全锥NAT、受限锥NAT、受限端口NAT,但不适用于对称螺母。 现在,我实现了一个模拟UDP打孔的小软件,这是代码(我知道它并不优雅,但现在我只想看看这个过程是否有效): 客户:Java 确定NAT类型和对称NAT上的端口预测,java,sockets,networking,nat,hole-punching,Java,Sockets,Networking,Nat,Hole Punching,首先很抱歉我的英语不好,但它不是我的第一语言。 我的问题是:我正在使用Java实现一个简单的桌面共享软件。第一个问题是如何与NAT后面的两个客户端通信。通过对Internet的一些研究,我发现一个更好的解决方案是使用UDP打孔技术。 如果我理解正确,此函数的工作方式如下: A=客户1 NA=客户端1的nat B=客户2 NB=客户端2的nat S=公共服务器 1-A向S发送UDP数据包 2-S接收公共IP和NA的公共端口 3-B向S发送UDP数据包 4-S接收公共IP和NB的公共端口 5-S通过
import java.io.IOException;
import java.net.*;
public class JavaClient {
public static void main(String[] args) throws UnknownHostException, SocketException, IOException {
new JavaClient().sendHostInfoToS();
}
private void sendHostInfoToS() {
// TODO code application logic here
byte[] buffer = {
99,99,99,99,99,99,99
} ;
InetAddress address;
try {
//Client contacts server
address = InetAddress.getByName("X.XXX.XX.XXX"); //SERVER
DatagramPacket packet = new DatagramPacket(
buffer, buffer.length, address, 9999
);
DatagramSocket datagramSocket = new DatagramSocket();
datagramSocket.send(packet);
System.out.println(InetAddress.getLocalHost().getHostAddress());
/////////////////////////////////////
//Client waits that server send back a UDP packet containing the information of the second client
DatagramPacket receivePacket = new DatagramPacket(new byte[40], 40);
System.out.println("WAIT RESPONSE...");
datagramSocket.receive(receivePacket);
System.out.println("RESPONSE RECEIVED...");
String sentence = new String( receivePacket.getData());
System.out.println("RECEIVED: " + sentence);
System.out.println("CODING IP...");
Thread.sleep(3000);
String[] p = sentence.split(":");
p[0] = p[0].replace("/","");
System.out.println("END CODING...");
/////////////////////////////////
//Client send to the other client a UDP packet using the information received from the server
byte[] sendData = new byte[8];
String sendString ="test";
sendData = sendString.getBytes();
DatagramPacket sPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName(p[0]), Integer.parseInt(p[1].substring(0,5)));
System.out.println("SENDING DATA TO CLIENT " + sPacket.getSocketAddress() + " on port " + Integer.toString(sPacket.getPort()));
DatagramSocket sDatagramSocket = new DatagramSocket();
//datagramSocket = new DatagramSocket();
sDatagramSocket.send(sPacket);
Thread.sleep(500);
System.out.println("DATA SENT...");
/////////////////////////////////////////////
//Client wait a response from the other client
receivePacket = new DatagramPacket(new byte[128], 128);
System.out.println("WAIT RESPONSE...");
//datagramSocket = new DatagramSocket();
sDatagramSocket.receive(receivePacket);
System.out.println("RESPONSE RECEIVED...");
sentence = new String( receivePacket.getData());
System.out.println("RECEIVED: "+ sentence);
/////////////////////////////////
}
catch (Exception e) {
e.printStackTrace();
}
}
}
服务器:
import java.io.IOException;
import java.net.*;
public class JavaServer {
DatagramPacket [] hosts = new DatagramPacket[2];
public static void main(String[] args) throws UnknownHostException, SocketException, IOException {
new JavaServer().waitInUDPConn();
}
DatagramSocket serverSocket;
private void waitInUDPConn() {
try {
serverSocket = new DatagramSocket(9999);
byte[] receiveData = new byte[8];
int indHosts = 0;
while(true) {
DatagramPacket receivePacket = new DatagramPacket(receiveData, receiveData.length);
System.out.println("WAITING DATA...");
serverSocket.receive(receivePacket);
String sentence = new String( receivePacket.getData());
System.out.println("RECEIVED: "+ sentence);
hosts[indHosts] = receivePacket;
indHosts++;
if (indHosts == 2) break;
}
} catch (Exception e) {
}
System.out.println("Hosts 1: "+ hosts[0].getAddress().toString() +", on port "+ Integer.toString(hosts[0].getPort()));
System.out.println("Hosts 2: "+ hosts[1].getAddress().toString() +", on port "+ Integer.toString(hosts[1].getPort()));
sendInfoToC(hosts[0], hosts[1]);
sendInfoToC(hosts[1], hosts[0]);
}
private void sendInfoToC(DatagramPacket dpDest, DatagramPacket dpInfoB) {
try {
byte[] sendData = new byte[8];
System.out.println("START WAIT...");
Thread.sleep(3000);
System.out.println("STOP WAIT...");
InetAddress IPAddress = dpDest.getAddress();
String sendString =dpInfoB.getAddress().toString() +":"+ Integer.toString(dpInfoB.getPort());
sendData = sendString.getBytes();
DatagramPacket sendPacket = new DatagramPacket(sendData, sendData.length, IPAddress, dpDest.getPort());
System.out.println("SENDING PACKET...");
serverSocket.send(sendPacket);
System.out.println("PACKET SENT...");
}
catch (Exception e) {
}
}
}
有了这段代码,我可以与服务器和两个客户端联系,服务器从两个客户端接收数据包,服务器向A发送B的信息,向B发送A的信息,两个客户端都接收信息,但它们不能一起通信。A没有从B接收数据包,B也没有从A接收数据包。所以现在我需要了解我是否支持两个客户端的对称nat。如果我了解其他类型的NAT(全锥、受限锥和端口受限锥),它们对每个外部连接总是使用相同的端口。因此,例如,如果我有一台IP为192.168.0.50的内部pc,并且这台机器想要达到一个公共IP(例如54.66.18.22),NAT将在数据包处分配公共IP和一个外部端口(例如端口58000)。稍后,如果同一台pc(192.168.0.50)想要连接另一台外部主机,NAT始终使用相同的外部端口58000。相反,使用对称NAT时,每个连接将具有不同的外部端口(第一个连接端口58000,第二个连接具有不同的端口)。现在,如果这是正确的,我认为这两个客户端都支持两个对称NAT。我可以确定这一点,因为从客户端A连接到服务器时,我看到服务器接收NAT A的公共IP和一个端口,例如55000。如果我关闭客户端并再次运行它,这次我看到服务器总是接收相同的IP(显然),但端口不是55000而是55001,如果我重试,端口是55002,依此类推。第二个客户机也是一样,从一个连接到另一个连接的端口增加1。现在,如果我理解了整个场景,这应该可以确认两个客户端都支持对称NAT。但有趣的是,两个NAT似乎都使用一个单位的增量,因此我认为当我从a向B和从B向a发送数据包时,我可以使用端口预测,所以我简单地尝试了以下方法:
DatagramPacket sPacket = new DatagramPacket(sendData, sendData.length, InetAddress.getByName(p[0]), Integer.parseInt(p[1].substring(0,5))+1);
我将1添加到从服务器接收到的端口,因为如果为真,我在两个对称NAT后面,那么也为真,增量为1个端口。请举例说明:
A连接到服务器并NAT A向S发送包含以下内容的数据包:45.89.66.125:58000
B连接到服务器,NAT B向S发送包含以下内容的数据包:144.85.1.18:45000
S将B的信息发送给A,将A的信息发送给B
现在,如果A向B发送此信息,NAT A将创建此映射:
内部地址:58001-144.85.1.18:45001
对于此连接,NAT A应使用端口58001(最后一个端口+1为对称NAT)
NAT B接收数据包,但丢弃它
现在,如果B向a发送包含接收到的信息的数据包,NAT B将创建此映射:
内部地址:45001-45.89.66.125:58001
现在NAT应该接受这个包,因为在它的表中有接受它的信息
但在这种情况下,没有一个客户从另一个客户那里收到信息。
我做错了什么?或者说我还不明白什么?理论到目前为止看起来不错,除了一点:在寻址另一个ip时,您不能100%确定端口增加了1。因此A和B应该使用多个目标端口触发更多数据包。那么让我们先假设A在portB+[0,n]向B发送数据包,然后B在portA+[0,n]向A发送数据包。如果A收到一个,你就有了一个匹配项。是的,最后这就是解决方案。我从A到S和从B到S发送了更多的数据包(3个数据包),通过这种技术,我可以确定端口间隔。谢谢你的回复