2011-11-27 106 views
1

我在從netcat或我的客戶端將UDP數據包發送到偵聽廣播UDP數據包的UDP服務器時遇到了一些問題。問題是我無法重新初始化緩衝區socket.receive(packet);並且當您檢查我的控制檯輸出時,您將看到數據包被髮送或接收兩次或甚至更多次並且最令人討厭的是當我發送一個數據包時首先是更大的長度,下一個更小的是以前的的一部分! (問題在控制檯輸出上標有HERE)我的客戶端和服務器位於同一LAN上。如何重新初始化數據包的緩衝區?

客戶端代碼:

DatagramSocket socket = new DatagramSocket(); 
socket.setBroadcast(true); 
byte[] buf = ("Hello from Client").getBytes(); 
byte[] buf2 = ("omg").getBytes(); 
DatagramPacket packet = new DatagramPacket(buf, buf.length, getBroadcastAddress(UDPConnection.context), Server.SERVERPORT); 
DatagramPacket packet2 = new DatagramPacket(buf2, buf2.length, getBroadcastAddress(UDPConnection.context), Server.SERVERPORT); 
Log.d("UDP", "C: Sending: '" + new String(buf) + "'"); 
socket.send(packet); 
socket.send(packet2); 

Server代碼:

void run(){ 
MulticastSocket socket = new MulticastSocket(SERVERPORT); 
socket.setBroadcast(true); 
byte[] buf = new byte[1024]; 
DatagramPacket packet = new DatagramPacket(buf, buf.length); 
while(true){ 
    Log.d("UDP", "S: Receiving..."); 
    socket.receive(packet); 
    //socket.setReceiveBufferSize(buf.length); 
    packet.setData(buf); 
    Log.i("BUFFER_packet",packet.getLength()+""); 
    Log.i("BUFFER_socket",socket.getReceiveBufferSize()+""); 
    Log.d("UDP", "S: From: " + packet.getAddress().getHostAddress()); 
    Log.d("UDP", "S: Received: "+getRidOfAnnoyingChar(packet)); 
    Log.d("UDP", "S: Done."); 
} 
} 
    //this method is getting rid of the "questionmark in a black diamond" character 
    public String getRidOfAnnoyingChar(DatagramPacket packet){ 
     Log.i("UDP","Inside getridofannoyingchar method."); 
     String result = new String(packet.getData()); 
     char[] annoyingchar = new char[1]; 
     char[] charresult = result.toCharArray(); 
     result = ""; 
     for(int i=0;i<charresult.length;i++){ 
      if(charresult[i]==annoyingchar[0]){ 
       break; 
      } 
      result+=charresult[i]; 
     } 
     return result; 
    } 

控制檯:

11-27 18:15:27.515: D/UDP(15242): S: Connecting... 
11-27 18:15:27.519: I/ServerIP(15242): :: 
11-27 18:15:27.519: I/LocalIP(15242): 192.168.0.4 
11-27 18:15:27.523: D/UDP(15242): S: Receiving... 
11-27 18:15:28.031: D/UDP(15242): C: Connecting... 
11-27 18:15:28.039: I/BroadcastIP(15242): 192.168.0.255 
11-27 18:15:28.042: I/BroadcastIP(15242): 192.168.0.255 
11-27 18:15:28.070: D/UDP(15242): C: Sending: 'Hello from Client' 
11-27 18:15:28.074: I/BUFFER_packet(15242): 1024 
11-27 18:15:28.074: I/BUFFER_socket(15242): 110592 
11-27 18:15:28.074: D/UDP(15242): S: From: 192.168.0.4 
11-27 18:15:28.074: I/UDP(15242): Inside getridofannoyingchar method. 
11-27 18:15:28.078: I/BUFFER_packet(15242): 1024 
11-27 18:15:28.078: I/BUFFER_socket(15242): 110592 
11-27 18:15:28.078: D/UDP(15242): S: From: 192.168.0.4 
11-27 18:15:28.078: I/UDP(15242): Inside getridofannoyingchar method. 
11-27 18:15:28.085: D/UDP(15242): S: Received: Hello from Client <------------HERE 
11-27 18:15:28.085: D/UDP(15242): S: Done. 
11-27 18:15:28.085: D/UDP(15242): S: Receiving... 
11-27 18:15:28.085: D/UDP(15242): S: Received: Hello from Client <------------HERE 
11-27 18:15:28.085: D/UDP(15242): S: Done. 
11-27 18:15:28.085: D/UDP(15242): S: Receiving... 
11-27 18:15:28.085: I/BUFFER_packet(15242): 1024 
11-27 18:15:28.085: I/BUFFER_socket(15242): 110592 
11-27 18:15:28.085: D/UDP(15242): S: From: 192.168.0.4 
11-27 18:15:28.085: I/UDP(15242): Inside getridofannoyingchar method. 
11-27 18:15:28.089: D/UDP(15242): S: Received: omglo from Client <------------HERE 
11-27 18:15:28.089: D/UDP(15242): S: Done. 
11-27 18:15:28.089: D/UDP(15242): S: Receiving... 
11-27 18:15:28.089: I/BUFFER_packet(15242): 1024 
11-27 18:15:28.089: I/BUFFER_socket(15242): 110592 
11-27 18:15:28.089: D/UDP(15242): S: From: 192.168.0.4 
11-27 18:15:28.089: I/UDP(15242): Inside getridofannoyingchar method. 
11-27 18:15:28.089: D/UDP(15242): S: Received: omglo from Client <------------HERE 
11-27 18:15:28.089: D/UDP(15242): S: Done. 
11-27 18:15:28.089: D/UDP(15242): S: Receiving... 
11-27 18:15:28.089: D/UDP(15242): C: Sent. 
11-27 18:15:28.089: D/UDP(15242): C: Done. 

任何幫助將不勝感激! :)

PS。控制檯中可能會有一些輸出,如完成/發送/連接/接收,但未添加到我的示例代碼中,但所有Received:/ BUFFER_packet/_socket/From都存在。

回答

5

您不需要重新初始化數據包中的緩衝區,只需將緩衝區的內容重置爲最初的任何內容(即您需要用零填充接收數組)。

的呼叫:

Arrays.fill(BUF,(字節)0);

在服務器端將重置數組爲零,因爲Java中的數組是通過引用而不是按值傳遞的(即您對數組內容的引用與DatagramPacket具有的引用相同,因此您可以在不通過DatagramPacket方法的情況下對其進行修改)。

話雖如此,你串行/解串數據的方式並不理想。最好使用ByteArrayOutputStream和ByteArrayInputStream封裝在發送和接收緩衝區周圍,然後用一個DataOutputStream/DataInputStream包裹這些緩衝區。這些將允許您以一種定義良好的格式編寫和讀取字符串,該格式可能會存儲字符串的長度,以便緩衝區上的任何剩餘數據都將被忽略。以這種方式適當地串行化或去串行化也將消除去除「黑鑽石」字符的需要。

DataOutputStream.writeUTF

如果你有興趣在這背後是你使用java.lang.String中的默認的序列做推理(的getBytes()和新的字符串(字節[])),以及如何正在填充UDP數據包。我將嘗試將它歸結爲關鍵位:

Java對String對象的內部表示不是字節數組 - 它是一個char數組。 Java字符與字節不同 - 一個字符實際上是2個字節,因爲它需要能夠表示不僅僅是拉丁字母(acbd ...),它需要支持來自其他語言/文化的其他字符,比如來自西里爾文,漢字等,一個字節是不夠的(一個字節讓你256個可能性,2個字節讓你65536個可能性)。

因此,當您調用getBytes()時,Java必須使用一些'scheme'(編碼)將該字符數組轉換爲一個字節數組(序列化)。這些細節並不重要,但是當您發送第一個字節塊時(可以說它的長度爲10個字節),您將數據包讀入更大的緩衝區(1024字節)。然後你要求Java String去反序列化整個緩衝區,而不僅僅是10個字節。

該方案(編碼)不知道只處理前10個字節,所以它試圖解碼整個1024字節,然後你得到奇怪的字符在你的字符串像黑鑽石,或(你在哪裏'我們通過發送'hello'在10個字節後添加了一些其他數據),您將從前一個接收的字符混合到您的字符串中。

使用write/readUTF將寫入字節數組的長度以及數據,因此當您再次讀取它時,它會知道它只需要讀取前10個字符(或許多有效的字符)。

+0

感謝這個宏偉而詳細的答案!根據你寫的內容,我在這裏找到了一個使用ByteArrayOutputStream的示例:http://homepages.uel.ac.uk/2795l/pages/javaapps.htm 它現在可以工作得很好:) – Miky

+1

不客氣:)請記住, ByteArrayOutputStream也只是使用對數組的引用,所以每次讀取數據包時都不需要重新初始化它,只需調用'reset',它就會再次從數組的開頭進行寫操作。 – AntonyM