Android UDP 通信通信、Android、UDP

2023-09-07 12:44:16 作者:酷到炸的小仙女

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

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

一些基础知识:

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

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

我的代码很简单:

我的服务器(在我的 PC 上运行)正在侦听端口 8752 上的 UDP 流量.我的 Android 应用程序在一个随机端口上打开一个 DatagramSocket,并使用该端口向我的服务器发送一个数据包.然后我保存此信息(接收数据包的 InetAddress 和在数据包中找到的端口).我尝试从我的服务器(同样是在我的 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);
}

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

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

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();
        }
    }
}

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

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:

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.

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

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

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

Android App - Now listening for UDP traffic on port 8000

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

Android App - Sending packet to server

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

Server - Received packet from XXXXXX:33081

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

Server - Sending IDLE packet to XXXXXX:33081

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

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

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

Android App - Now listening for UDP traffic on port 8000

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

Android App - Sending packet to server

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

Server - Received packet from XXXXXX:8000

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

Server - Sending IDLE packet to XXXXXX:8000

Android 应用永远不会收到来自使用任一端口的任何 UDP 流量.

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

推荐答案

抱歉没有尽快更新.问题修复如下:

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

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

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.

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

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;
}

新的服务器监听代码:

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;