如何重新初始化数据包的缓冲区?

问题描述:

我在从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都存在。

您不需要重新初始化数据包中的缓冲区,只需将缓冲区的内容重置为最初的任何内容(即您需要用零填充接收数组)。

的呼叫:

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