2011-05-05 30 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打孔機相同的襪子的對象與計時器