Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/313.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/sockets/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 确定NAT类型和对称NAT上的端口预测_Java_Sockets_Networking_Nat_Hole Punching - Fatal编程技术网

Java 确定NAT类型和对称NAT上的端口预测

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通过

首先很抱歉我的英语不好,但它不是我的第一语言。 我的问题是:我正在使用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打孔的小软件,这是代码(我知道它并不优雅,但现在我只想看看这个过程是否有效):

客户:

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个数据包),通过这种技术,我可以确定端口间隔。谢谢你的回复