Android:NAT穿越?

10

在我的看法中,较新的Android设备似乎在NAT后面运行,其中本地地址是内部载体或LAN地址,公共地址是路由器或运营商分配的外部地址。

然而,新手机使用NetworkInterface时不会返回与访问IP检测服务时相同的地址。

因此,通过直接的P2P SocketChannels连接天然失败。

是否有为Android平台设计的常见解决方法来解决这个问题?是否有人可以澄清这种类似NAT的安全问题是什么导致的?

任何关于Java NAT穿透教程或示例(不是论文或论文)的链接也将被视为有用(因为我不太确定如何在Java中实现它)。

当然,我也会接受任何其他解决方案!


2
Android设备中没有内置的NAT功能。一个设备拥有一个或多个网络接口,如果你监听正确的接口,你就能从外部获得连接(假设你已经获取了互联网权限)。如果无法检查试图访问者和设备之间的网络路径,请注意。 - zapl
NAT与终端设备无关,设备只是被分配了一个IP地址。它是否可路由取决于接入点/DHCP服务器。 - Nick
这一定与终端设备有关。在使用移动网络接口地址测试直接P2P连接时,连接到旧的Android设备(Epic 4G)时可以正常工作,但连接到Galaxy S2时却不行。由NetworkInterface.getNetWorkInterfaces方法返回的Galaxy地址与外部IP不匹配,而Epic 4G的地址则匹配。显然,手机的网络设置发生了变化。 - bgroenks
是的,我同意zapi。我认为你得出了一个结论。我想知道几件事:1)在您的设备上分配给接口的地址有哪些;2)当您从设备向其他连接的设备发送数据包时,该数据包似乎来自哪个地址。当然,这与DHCP无关。 - G. Blake Meike
4个回答

6
几乎所有你所接触到的手机或电脑都没有静态公共IP地址,因此需要进行NAT穿透。这不是设备的问题,而是运营商或ISP在你的设备和公共互联网之间放置了路由器。根据你的应用程序,通常有可以使用的NAT穿透库,例如ice4jSTUNT

ice4j和STUNT都只支持UDP协议,对吗? - bgroenks

3

我在自己的项目中也做了这件事,并发现这个问题并不那么复杂。

以下是一个非常简单的Node.js UDP回显服务器示例:

var dgram = require('dgram');

var socket =
    dgram.createSocket('udp4');

socket
    .on('listening', function()
    {
        var address = socket.address();
        console.log('socket listening ' +
            address.address + ':' + address.port);
    })
    .on('error', function(err)
    {
        console.log('socket error:\n' + err.stack);
        socket.close();
    })
    .on('message', function(message, rinfo)
    {
        console.log('message: ' + message + ' from ' +
            rinfo.address + ':' + rinfo.port);

        var msg = new Buffer(rinfo.address + ':' + rinfo.port);
        socket
            .send(msg, 0, msg.length,
                rinfo.port, rinfo.address,
                function(err, bytes)
                {
                    //socket.close();
                });

    })
    .bind(15000);

一个Android客户端只需向此节点服务器发送一条消息。
System.out.println("UDP hole punching=======================");

class IOth extends Thread {
    @Override
    public void run() {

        String sendMsg = "UDP hole punching";
        byte[] buf = sendMsg.getBytes();
        DatagramPacket packet;

        System.out.println(HPremoteHost); // node server IP
        System.out.println(HPremotePort); // 15000
        try {
            packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(HPremoteHost), HPremotePort);
            ds.send(packet);
        } catch (Exception e) {
            System.out.println("error================");
            System.out.println(e);
        }
    }
}
IOth io00 = new IOth();
io00.start();

Android客户端UDP监听器,通过UDPholepunching获取通用消息和您自己的全局IP&端口

class IOLoop extends Thread {
    @Override
    public void run() {
        try {
            String msg = "Native.UDPserver.open";
            SocketAddress sockAddress;
            String address;
            byte[] buf = new byte[1024];
            DatagramPacket packet = new DatagramPacket(buf, buf.length);
            while (true) {
                try {
                    ds.receive(packet);
                    sockAddress = packet.getSocketAddress();
                    address = sockAddress.toString();

                    msg = new String(buf, 0, packet.getLength());

                    System.out.println(msg + "  received !!! by " + address);

                    // this case is UDP HolePunching reaction
                    if (address.equals(HPaddress1)) {
                        System.out.println(msg + "hole punched");

                        // So you can obtain own Global ip& port here.
                        // exchange this information
                        // `remoteHost` `remotePort` to another client
                        // with some method (signaling server)
                    }
                } catch (IOException e) {
                }
            }
        } catch (Exception e) {
        }
    }
}
IOLoop io00 = new IOLoop();
io00.start();

使用其他客户端的IP remoteHost 和端口号remotePort 发送Android客户端UDP。

class IOth extends Thread {
    @Override
    public void run() {
        String sendMsg = "This is a test message";
        byte[] buf = sendMsg.getBytes();
        DatagramPacket packet;

        try {
            packet = new DatagramPacket(buf, buf.length, InetAddress.getByName(remoteHost), remotePort);
            ds.send(packet);
        } catch (Exception e) {
        }
    }
}
IOth io00 = new IOth();
io00.start();

目的是回答问题。 - user1028880

1

这个应该写在注释里。 - Jules
我收集了所有PCP/NAT-PMP库的列表。https://en.wikipedia.org/wiki/Port_Control_Protocol#Implementations - Sergey Ponomarev

0

我已成功建立套接字,只需在路由器连接期间转发您使用的套接字即可。 这对我有效。

更新

如果您正在使用Windows(ipconfig)或Linux(ifconfig),请通过cmd.exe找出您的IP地址。然后通过浏览器连接它,应该会有一个安全部分。 转到端口转发并打开您在建立ServerSocket和Socket时使用的端口。 使用TCP作为协议。 请注意,仅当您尝试从wlan外部连接时才适用此选项。


你能否详细说明或提供一个例子?这个概念看起来很有意思。 - bgroenks
3
只是端口转发吗?NAT穿透的整个目的就是为了避免这种情况。 - bgroenks

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接