【问题标题】:Android can send udp but not receiveAndroid可以发送udp但不能接收
【发布时间】:2015-11-15 01:44:13
【问题描述】:

我编写了一个基本的 udp 客户端服务器,其中一个 android 向服务器发送消息,服务器将其中继给其余客户端。

问题是,传入的 udp 消息到达服务器,服务器将它们中继回来,但它们永远不会到达其余设备。

真正令人惊奇的是,如果我将服务器用作回显服务器(即仅转发给发件人),那么一切正常。所有客户端和服务器套接字使用相同的端口 2706

服务器代码

while (true) {
    DatagramPacket packetToReceive = new DatagramPacket(new byte[2048], 2048);
    try {
        listenerSocket.receive(packetToReceive);
        InetAddress senderAddress = packetToReceive.getAddress();
        relayedBytes += packetToReceive.getLength();

        if (!connectedClients.contains(senderAddress)) {
            connectedClients.add(senderAddress);
        }

        for (InetAddress addr : connectedClients) {
            // commenting this line will make it an echo server
            if (!addr.equals(senderAddress))
            {
                //The following actually prints the ips of the android
                //devices so it knows where to send

                System.out.println(senderAddress.getHostAddress().toString() +
                    " to " + addr.getHostAddress().toString());
                byte[] data = packetToReceive.getData();
                packetToReceive.setData(data, 0, packetToReceive.getLength());
                packetToReceive.setAddress(addr);
                listenerSocket.send(packetToReceive);
            }
        }
    } catch (IOException) {
        e.printStackTrace();
    }
}

android 发送者逻辑:

mainSocket=new DatagramSocket(homePort);

//targetAddressString is the public IP of server
target = InetAddress.getByName(targetAddressString);
while (true) {
    byte[] data = getdata();
    if (data == null)
        continue;
    DatagramPacket packet = new DatagramPacket(data, data.length, target, targetPort);

    mainSocket.send(packet);
}

同时在其他线程上,接收者只是用相同的 udp 套接字等待:

while (true) {
    Log.d("Player", "Waiting for data");
    DatagramPacket packet = new DatagramPacket(new byte[2048], 2048);
    try {
        mainSocket.receive(packet);
        Log.d("Player", "packet received");
        //do something with the packet
    } catch (IOException e) {
        e.printStackTrace();
    }
}

它永远不会比等待数据更进一步,因为它会阻塞直到它收到一个数据包

此外,我还可以在 wifi 和移动数据图标中看到它没有收到任何数据,但数据发送始终处于开启状态,并且在服务器上可以看到

**编辑:- 回声服务器 **

while (true) {
    DatagramPacket receivedPacket = new DatagramPacket(new byte[2048], 2048);
    try {
        listenerSocket.receive(receivedPacket);
        InetAddress senderAddress = receivedPacket.getAddress();
        if (!connectedClients.contains(senderAddress)) {
            connectedClients.add(senderAddress);
        }

        for (InetAddress addr : connectedClients) {
            byte[] data = receivedPacket.getData();
            DatagramPacket sendPacket= new DatagramPacket(data,  0, receivedPacket.getLength(), addr, receivedPacket.getPort());
            listenerSocket.send(sendPacket);
        }
    } catch (IOException e) {
        // ...
    }
}

基本上,它应该将消息转发给曾经发送过任何数据的每个客户端,但不知何故,它只将其发送给其原始发送者,其余客户端会错过它。代码在和我一起拖钓

【问题讨论】:

  • 这里发生了一些非常神奇的事情,我进一步简化了服务器,简单地将数据从一个客户端发送到 connectedClient 向量中的每个人。仍然只有最初发送数据包的人可以取回它。我认为我犯了一些非常愚蠢的错误。
  • 您能否将服务器的代码发布为似乎可以工作的回显服务器?这可能为我们回答提供必要的见解。
  • @AlainO'Dea 当然,请查看编辑

标签: java android sockets udp


【解决方案1】:

正如您所了解的,这是 NAT 穿越问题。

这里有一些提示:

  1. 服务器可以硬编码以侦听端口 2706。但它不应该对接收数据包的源端口进行任何假设。从您的代码来看,您似乎从未尝试调用setPort。因此,即使 NAT 没有以不同的方式映射您的端口号,我也不确定您的原始代码是如何设置任何目标端口的。但我认为您是根据自己的答案和更新的代码弄清楚这一点的。

  2. 不要在客户端的套接字上硬编码客户端端口。在您的套接字上选择端口“0”(或不设置),让操作系统为您选择第一个可用端口。如果您在同一个 NAT 后面有多个客户端,此行为将允许更快的客户端重新启动、多个设备位于同一 NAT 后面以及其他好处。

【讨论】:

  • @user1062760 我相信这是正确的答案,你应该接受它。
  • 是的,谢谢,实际上 2706 是一个测试用例编号,用于查看是否一切顺利,我认为我的手机有一个公共 IP 才是真正的问题,但我应该这样做我让操作系统将它绑定到可用端口,而不是硬编码它,即使让我一无所知也可以工作:P
  • 有什么方法可以强制设备监听我们的安卓应用程序的特定端口吗?
【解决方案2】:

霍莉莫莉!我终于明白了,这是我自己的错,我没有考虑我的手机提供商的 Nat,并且还假设公共 IP 中的 2706 端口。

事实证明,我实际上是在手机网络的 NAT 下,而我的端口 2706 在到达服务器时已转换为一些可怕的端口号。所以我不得不考虑实际收到的数据包的端口号,而不是手机上设置的端口号。

简而言之就是这样

手机(2706端口)-> NAT(40234端口)-> 服务器(40234端口)

所以我实际上是在尝试将数据发送回 2706 而不是 40234 -_-'

一旦我开始在 40234 (或任何随数据包附带的)而不是 2706 发回数据包,它就会优雅地沿着路径返回我的安卓手机,一切都很好。

【讨论】:

  • 有什么方法可以用服务器发送的相同端口接收?
  • 喜欢将特定应用程序的设备端口更改为硬编码端口?
  • 当然可以,单个端口可用于传输和接收任何唯一远程地址的数据
  • 没有,如果没有 sim,则有两个端口用于发送和接收,那么一切正常,但如果我们将 sim 放入 android 设备,就会出现上述问题。但是 ios 设备即使使用 sim 也能正常工作。是否有任何类似的解决方法,我们可以覆盖蜂窝网络端口并监听服务器发送广播的端口。
  • 如果没有 sim 则没有问题,但如果 sim 可用则 udp 数据包不会收到..
猜你喜欢
  • 1970-01-01
  • 2011-05-28
  • 2021-07-30
  • 2015-03-14
  • 2011-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多