java 安卓UDP通讯

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/5904820/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 13:21:47  来源:igfitidea点击:

Android UDP Communication

javaandroidudp

提问by someone1

I've read many posts on this site on how to receive UDP packets in Android. However, none of this is working for me!

我在这个网站上阅读了很多关于如何在 Android 中接收 UDP 数据包的帖子。然而,这些都不适合我!

Some basics:

一些基础知识:

I am testing on my HTC Incredible (Android 2.2) running on 3G (not wifi or anything else). No emulators are involved here.

我正在测试我的 HTC Incredible (Android 2.2) 在 3G 上运行(不是 wifi 或其他任何东西)。这里不涉及模拟器。

My code is simple:

我的代码很简单:

  1. My server (running on my PC) is listening for UDP traffic on port 8752.
  2. My Android application opens a DatagramSocket on a random port and sends a packet to my server with this port.
  3. I then save this information (the InetAddress form the received packet and the port found within the packet).
  4. I try to send an UDP packet from my server (again, on my PC) to my Android app (running on my phone) and it does NOT work.
  1. 我的服务器(在我的 PC 上运行)正在侦听端口 8752 上的 UDP 流量。
  2. 我的 Android 应用程序在一个随机端口上打开一个 DatagramSocket,并使用这个端口向我的服务器发送一个数据包。
  3. 然后我保存这些信息(InetAddress 来自收到的数据包和在数据包中找到的端口)。
  4. 我尝试从我的服务器(再次,在我的 PC 上)向我的 Android 应用程序(在我的手机上运行)发送一个 UDP 数据包,但它不起作用。
//Server code to initialize the UDP socket (snippet)
public void init() {
    datagram_server_socket = new DatagramSocket(port,local_addr);
    datagram_server_socket.setSoTimeout(1000);
}

//Snippet of code on the ANDROID APP that sends a packet to the server

//ANDROID APP上向服务器发送数据包的代码片段

public void connect() {
    Random r = new Random(System.currentTimeMillis());
    int udp_port = 0;
    while(true){
        try {
            udp_port = r.nextInt(1000)+8000;
            udp_port = 8000;
            comm_skt = new DatagramSocket(udp_port);
            Log.i("ServerWrapper", "UDP Listening on port: " + udp_port);
            break;
        } catch(SocketException e) {
            Log.e("ServerWrapper", "Could not bind to port " + udp_port);
        }
    }
    byte[] sdata = new byte[4+tid.length];
    i = 0;
    sdata[i++] = (byte)(0XFF&(udp_port>>24));
    sdata[i++] = (byte)(0XFF&(udp_port>>16));
    sdata[i++] = (byte)(0XFF&(udp_port>>8));
    sdata[i++] = (byte)(0XFF&(udp_port));
    for(byte b: tid){
        sdata[i++] = b;
    }
    DatagramPacket pkt = new DatagramPacket(sdata, sdata.length, 
                                InetAddress.getByName(hostname), port);
    comm_skt.send(pkt);
}
//Server's UDP socket listening code
public void serverUDPListener() {
    try {
        datagram_server_socket.receive(rpkt);
        int port = 0;
        byte[] rdata = rpkt.getData();
        port += rdata[0]<<24;
        port += rdata[1]<<16;
        port += rdata[2]<<8;
        port += (0XFF)&rdata[3];
        byte[] tid = new byte[rdata.length];
        for(int i = 4; i < rdata.length && rdata[i] > 0; i++) {
            tid[i-4] = rdata[i];
        }
        String thread_id = new String(tid).trim();
        for(int i = 0; i < threads.size(); i++) {
        ClientThread t = threads.get(i);
        if(t.getThreadId().compareTo(thread_id) == 0) {
            t.setCommSocket(rpkt, port);
        } else {
            System.err.println("THREAD ID " + thread_id + " COULD NOT BE FOUND");
        }
        }
    } catch (IOException e) {
        if(!(e instanceof SocketException) && !(e instanceof SocketTimeoutException))
        log.warning("Error while listening for an UDP Packet.");
    }
}
//Corresponds to the setCommSocket call above to save the IP and Port of the incoming UDP packet on the server-end
public void setCommSocket(DatagramPacket pkt, int port) {
    comm_ip = pkt.getAddress();
    comm_port = pkt.getPort(); //Try the port from the packet?
}
//Sends an UDP packet from the SERVER to the ANDROID APP
public void sendIdle() {
    if(comm_ip != null) {
        System.err.println("Sent IDLE Packet (" + comm_ip.getHostAddress() + ":" + comm_port + ")");
        DatagramPacket spkt = new DatagramPacket(new byte[]{1, ProtocolWrapper.IDLE}, 2, comm_ip, comm_port);
        DatagramSocket skt;
        try {
            skt = new DatagramSocket();
            skt.send(spkt);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Right now I've hard coded the port my application uses to 8000. However, what's odd is that EVERYTIME I test my program (and view the IP/Port that is saved on my server), the port the packet came from is always 33081. I have a a thread constantly listening for UDP traffic in my Android App but the code never executes passed the "receive(packet)" part:

现在我已经将我的应用程序使用的端口硬编码为 8000。然而,奇怪的是每次我测试我的程序(并查看保存在我的服务器上的 IP/端口),数据包来自的端口总是 33081 . 我有一个线程在我的 Android 应用程序中不断侦听 UDP 流量,但代码从未通过“接收(数据包)”部分执行:

public void AndroidUDPListener() {
    while(true) {
        synchronized(stop) {
        if(stop) return;
        }
        byte[] recieve_data = new byte[64];
        DatagramPacket rpkt = new DatagramPacket(recieve_data, recieve_data.length);
        try {
        if(comm_skt == null) 
                continue;
        comm_skt.receive(rpkt);
        byte[] data = rpkt.getData();
        switch(data[1]) {
            case IDLE:
            if(ocl != null) ocl.onCompletion(null);
            break;
            case KEEP_ALIVE:
            break;
        }
        } catch (Exception e) {
        if(!(e instanceof SocketException) && !(e instanceof SocketTimeoutException))
                Log.w("ServerWrapper", "Error while listening for an UDP Packet.");
        }
    }
}

Does anyone see an issue in my code? Or is there some permission/settings I need to set on my application first? I have internet communication enabled.

有人在我的代码中看到问题吗?或者我需要先在我的应用程序上设置一些权限/设置吗?我启用了互联网通信。

Example Output (using the port from the packet getPort()):

示例输出(使用数据包 getPort() 中的端口):

Android App - Now listening for UDP traffic on port 8000

Android App - Sending packet to server

Server - Received packet from XXXXXX:33081

Server - Sending IDLE packet to XXXXXX:33081

Android 应用程序 - 现在侦听端口 8000 上的 UDP 流量

Android 应用程序 - 向服务器发送数据包

服务器 - 收到来自 XXXXXX:33081 的数据包

服务器 - 向 XXXXXX:33081 发送 IDLE 数据包

Example Output (using the port from the packet data):

示例输出(使用数据包数据中的端口):

Android App - Now listening for UDP traffic on port 8000

Android App - Sending packet to server

Server - Received packet from XXXXXX:8000

Server - Sending IDLE packet to XXXXXX:8000

Android 应用程序 - 现在侦听端口 8000 上的 UDP 流量

Android 应用程序 - 向服务器发送数据包

服务器 - 收到来自 XXXXXX:8000 的数据包

服务器 - 向 XXXXXX:8000 发送 IDLE 数据包

The Android App never receives any UDP traffic from using either of the ports.

Android 应用程序永远不会从使用任何一个端口接收任何 UDP 流量。

采纳答案by someone1

Sorry for not updating this sooner. The problem was fixed as follows:

抱歉没有早点更新这个。问题修复如下:

I needed to store the DatagramSocket to each thread. The listening socket should also be the socket used to continue communication between the server and client. Here are the bits of updated code.

我需要将 DatagramSocket 存储到每个线程。侦听套接字也应该是用于在服务器和客户端之间继续通信的套接字。以下是更新后的代码。

New socket registration code on thread:

线程上的新套接字注册代码:

public void setCommSocket(DatagramPacket pkt, int port, DatagramSocket skt)
{
  comm_ip = pkt.getAddress();
  comm_port = pkt.getPort();
  synchronized(comm_pkt) {
    comm_pkt = pkt;
  }
  comm_skt = skt;
}

New Server Listening Code:

新的服务器监听代码:

public void UDPListen() {
        while(true) {
            synchronized(stop) {
                if(stop)
                    break;
            }

            byte[] recieve_data = new byte[64];
            DatagramPacket rpkt = new DatagramPacket(recieve_data, recieve_data.length);
            try {
                datagram_server_socket.receive(rpkt);
                int port = 0;
                byte[] rdata = rpkt.getData();
                port += rdata[0]<<24;
                port += rdata[1]<<16;
                port += rdata[2]<<8;
                port += (0XFF)&rdata[3];
                byte[] tid = new byte[rdata.length];
                for(int i = 4; i < rdata.length && rdata[i] > 0; i++)
                {
                    tid[i-4] = rdata[i];
                }
                String thread_id = new String(tid).trim();
                for(int i = 0; i < threads.size(); i++) {
                    ClientThread t = threads.get(i);
                    if(t.getThreadId().compareTo(thread_id) == 0)
                    {
                        t.setCommSocket(rpkt, port, datagram_server_socket);
                    } else {
                        System.err.println("THREAD ID " + thread_id + " COULD NOT BE FOUND");
                    }
                }
            } catch (IOException e) {
                if(!(e instanceof SocketException) && !(e instanceof SocketTimeoutException))
                    log.warning("Error while listening for an UDP Packet.");
            } finally {
                for(int i = 0; i < threads.size(); i++) {
                    ClientThread t = threads.get(i);
                    t.sendKeepAlive();
                }
            }
        }
    }

There was some update to the structure of the server/threads that I will omitt. The important part here is that the socket in which the packet was recieved with was re-used to send data back to the client. Additionally, the actual packet was re-used to send data back:

我将省略对服务器/线程结构的一些更新。这里的重要部分是接收数据包的套接字被重新用于将数据发送回客户端。此外,实际的数据包被重新用于发回数据:

public void sendIdle() {
        if(comm_ip != null) {
            synchronized(comm_pkt) {
                try {
                    comm_pkt.setData(new byte[]{1, ProtocolWrapper.IDLE});
                    comm_skt.send(comm_pkt);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }

    }

Here is the relevant parts of my wrapper class that shows what each thread was holding:

这是我的包装类的相关部分,显示了每个线程所持有的内容:

public class PeerWrapper {

    private InetAddress ipaddress;
    private Integer port;
    private Socket client_socket;
    private InetAddress comm_ip;
    private DatagramSocket comm_skt;
    private DatagramPacket comm_pkt;
    private int comm_port;
    private byte status;

回答by Flynny75

I had a similar problem. On the Android was two sockets (sending/listening), and on a PC server was again two sockets (sending/listening). The phone would ping the PC's known listening socket with the address of the phone's unknown listening socket, so the PC could reply. Nothing I was doing appeared to be getting the address of the listening socket, as the socket would never receive anything.

我有一个类似的问题。在 Android 上有两个套接字(发送/收听),而在 PC 服务器上又是两个套接字(发送/收听)。电话将使用电话未知侦听套接字的地址 ping PC 的已知侦听套接字,以便 PC 可以回复。我所做的一切似乎都没有得到监听套接字的地址,因为套接字永远不会收到任何东西。

This solved my problem: Android: java.net.DatagramSocket.bind: Invalid Argument Exception. Use a channel to create the socket, then binding on null. Now I can use the sending socket on the phone to send a packet containing the port of the listening socket (the IPs are the same) to the PC, obtained with .getLocalPort()The PC reads the byte[], gets the port, and sends packets back to the phones listening port.

这解决了我的问题:Android: java.net.DatagramSocket.bind: Invalid Argument Exception。使用通道创建套接字,然后绑定到 null。现在我可以使用手机上的发送套接字向PC发送包含监听套接字端口(IP相同)的数据包,用.getLocalPort()PC读取byte[],获取端口,然后将数据包发回到电话监听端口。

回答by rtdff

android have inbound firewall

android有入站防火墙

you have to use first udop hole punch same sock object with timer

你必须使用第一个 udop 打孔器和计时器相同的袜子对象