Android UDP通讯

时间:2011-05-05 22:28:01

标签: java android udp

我在本网站上阅读了很多关于如何在Android中接收UDP数据包的帖子。但是,这对我来说都不起作用!

一些基础知识:

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

我的代码很简单:

  1. 我的服务器(在我的电脑上运行)正在侦听端口8752上的UDP流量。
  2. 我的Android应用程序在随机端口上打开一个DatagramSocket,并使用此端口将数据包发送到我的服务器。
  3. 然后我保存此信息(InetAddress形成接收的数据包和数据包中找到的端口)。
  4. 我尝试从我的服务器(再次,在我的电脑上)发送UDP数据包到我的Android应用程序(在我的手机上运行),但它不起作用。
  5. //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上的代码片段,用于将数据包发送到服务器

    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流量,但代码从未执行通过“接收(数据包)”部分:

    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.");
            }
        }
    }
    

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

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

      

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

         

    Android App - 将数据包发送到服务器

         

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

         

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

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

      

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

         

    Android App - 将数据包发送到服务器

         

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

         

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

    Android App从未使用任何一个端口接收任何UDP流量。

3 个答案:

答案 0 :(得分:3)

很抱歉没有及早更新。问题修复如下:

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

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

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

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

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

    }

以下是我的包装器类的相关部分,它显示了每个线程持有的内容:

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;

答案 1 :(得分:2)

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

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

答案 2 :(得分:1)

android有入站防火墙

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