当我从netcat或我的客户端向我的UDP服务器发送UDP数据包时,我遇到了一些问题,该服务器监听广播UDP数据包.问题是我无法重新启动And的缓冲区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);
服务器代码:
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安慰:
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.控制台中可能有一些输出,例如Done/Sent/Connecting/Receiving,但未添加到我的示例代码中,但所有Received:/ BUFFER_packet/_socket/From都存在.
1> AntonyM..:你不需要重新初始化数据包中的缓冲区,你只需要将缓冲区的内容重置为最初的任何内容(即你需要用零填充接收数组).
致电:
Arrays.fill(BUF,(字节)0);
在服务器端将数组重置为零,因为Java中的数组是按引用传递而不是按值传递(即,您对数组内容的引用与DatagramPacket具有的引用相同,因此您可以无需经过DatagramPacket方法即可修改它.
已经说过你对数据进行序列化/反序列化的方式并不理想.你最好使用ByteArrayOutputStream和ByteArrayInputStream包裹发送和接收缓冲区,然后使用DataOutputStream/DataInputStream.这些将允许您以明确定义的格式写入和读取字符串,这可能存储字符串的长度,以便无论如何都会忽略缓冲区上的任何剩余数据.以这种方式正确地序列化/反序列化也将消除剥离"黑钻石"字符的需要.
DataOutputStream.writeUTF
如果你对这背后的原因感兴趣,那就是使用java.lang.String的默认序列化(getBytes()和new String(byte []))以及如何填充UDP数据包.我会尝试把它归结为关键点:
Java的String对象的内部表示不是字节数组 - 它是一个char数组.Java的字符是不一样的字节 - 一个字符实际上是2个字节,因为它需要能够代表的不仅仅是拉丁字母以上(ACBD ......),它需要支持从像的东西其他语言/文化其它字符西里尔文,汉字等和一个字节是不够的(一个字节可以获得256种可能性,2个字节可以获得65536种可能性).
因此,当您调用getBytes()时,Java必须使用一些"scheme"(编码)将该字符数组转换为字节数组(序列化).个中详情没有太大的关系,但是当你发送字节的第一个块,(可以说其10个字节长)你读了包成一个更大的缓冲区(1024个字节).然后,您要求Java String对整个缓冲区进行反序列化,而不仅仅是10个字节.
该方案(编码)不知道,只与前10个字节处理,因此试图将整个1024个字节进行解码,然后你在你的字符串,如黑钻,或(如你已经把其他一些变得怪异字符通过发送'hello'在10个字节之后的数据,你将前一个接收的字符混合到你的字符串中.
使用写/的readUTF将写入字节数组,以及数据的长度,所以当你读一遍就知道它只有读取前10个字符(或然而,许多是有效的).