2011-05-05 90 views
5

我在这个网站上读过很多关于如何在Android中接收UDP数据包的文章。但是,这些都不适合我!Android的UDP通信

一些基本知识:

我测试我的HTC不可思议的(Android 2.2的)在3G(没有WiFi或其他任何东西)上运行。这里没有模拟器参与。

我的代码很简单:

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

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.我有AA线不断地监听UDP流量在我的Android应用程序,但该代码永远不会执行通过的“接收(数据包)”部分:

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应用 - 现在监听UDP通信端口8000

Android应用 - 发送分组到服务器

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

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

实施例产量(使用的端口从分组数据):

Android应用 - 现在监听UDP通信端口8000

Android应用 - 发送分组到服务器

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

服务器 - 发送空闲分组到XXXXXX:8000

Android应用程序永远不会使用任何端口接收任何UDP通信。

+0

你能得到这个工作在两个台式机/笔记本电脑(没有你的Android手机)?如果您可以将手机作为问题消除,那么我们可以认为这是您的代码问题。 – Kiril 2011-05-05 23:40:09

+0

我能够将手机放在我的WiFi网络上,并以这种方式获得UDP数据包(使用直接IP作为主机名,使用外部DNS名称不起作用)。我还让我的朋友测试了我编写的一个程序,用于在家中模拟Android的应用程序网络调用(使用外部DNS名称),并且它工作正常(在进行端口转发并强制使用非随机UDP端口之后)。我希望这能澄清问题是我的代码。 – someone1 2011-05-06 03:37:41

回答

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

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

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

+0

你好,谢谢你的回答。我不是100%确定它是否能解决我的问题,但是我在一年前完成了这个项目(并解决了我的问题),并没有时间来测试它是否有效。 – someone1 2012-10-03 04:21:32

1

的Android有入站防火墙

,你必须先用UDOP打孔机相同的袜子的对象与计时器