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

Java 如何重新初始化数据包的缓冲区?,java,android,string,sockets,udp,Java,Android,String,Sockets,Udp,当我从netcat或我的客户端向我的UDP服务器发送UDP数据包时,我遇到了一些问题,该服务器侦听广播UDP数据包。问题是我不能重新初始化socket.receive(packet)的缓冲区当您检查我的控制台输出时,您将看到数据包被发送或接收了两次或更多次,最烦人的是当我首先发送长度较长的数据包时,下一个较小的数据包是前一个数据包的一部分!(问题在控制台输出上标记为HERE)我的客户端和服务器位于同一局域网上 客户端代码: DatagramSocket socket = new Datagram

当我从netcat或我的客户端向我的UDP服务器发送UDP数据包时,我遇到了一些问题,该服务器侦听广播UDP数据包。问题是我不能重新初始化socket.receive(packet)的缓冲区当您检查我的控制台输出时,您将看到数据包被发送或接收了两次或更多次,最烦人的是当我首先发送长度较长的数据包时,下一个较小的数据包是前一个数据包的一部分!(问题在控制台输出上标记为HERE)我的客户端和服务器位于同一局域网上

客户端代码:

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<charresult.length;i++){
            if(charresult[i]==annoyingchar[0]){
                break;
            }
            result+=charresult[i];
        }
        return result;
    }
void run(){
MulticastSocket=新的MulticastSocket(服务器端口);
插座。路演(真实);
字节[]buf=新字节[1024];
DatagramPacket数据包=新的DatagramPacket(buf,buf.长度);
while(true){
Log.d(“UDP”,“S:接收…”);
套接字接收(数据包);
//socket.setReceiveBufferSize(基本长度);
数据包设置数据(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:”+getridofanoyngchar(数据包));
Log.d(“UDP”,“S:Done.”);
}
}
//这个方法就是去掉“黑钻石上的问号”字符
公共字符串GetRidofAnoyingChar(DatagramPacket数据包){
i(“UDP”,“内部getridofanoyngchar方法”);
字符串结果=新字符串(packet.getData());
char[]烦扰char=新字符[1];
char[]charresult=result.toCharArray();
结果=”;

对于(int i=0;i您不需要重新初始化数据包中的缓冲区,只需将缓冲区的内容重置为初始值(即,您需要用零填充接收数组)

呼吁:

数组.fill(buf,(字节)0)

在服务器端,会将数组重置为零,因为Java中的数组是按引用传递的,而不是按值传递的(即,您对数组内容的引用与DatagramPacket的引用相同,因此您可以在不使用DatagramPacket方法的情况下对其进行修改)

话虽如此,序列化/反序列化数据的方式并不理想。最好在发送和接收缓冲区周围使用ByteArrayOutputStream和ByteArrayInputStream,然后在这些缓冲区周围使用DataOutputStream/DataInputStream。这将允许您在定义良好的环境中写入和读取字符串d格式,该格式可能存储字符串的长度,因此缓冲区中的任何剩余数据都将被忽略。以这种方式正确序列化/反序列化也将消除删除“黑钻石”字符的需要

如果您对这背后的原因感兴趣,那么这与您使用java.lang.String的默认序列化(getBytes()和new String(byte[])以及UDP数据包的填充方式有关。我将尝试将其归结为关键点:

Java对字符串对象的内部表示不是字节数组,而是字符数组。Java字符与字节不同,一个字符实际上是两个字节,因为它需要能够表示的不仅仅是拉丁字母表(acbd…),它需要支持来自其他语言/文化的其他字符,如西里尔文、汉字等,而一个字节是不够的(一个字节表示256种可能性,两个字节表示65536种可能性)

因此,当您调用getBytes()时,Java必须使用某种“方案”(编码)将该字符数组转换为字节数组(序列化)。这方面的细节并不重要,但当您发送第一个字节块(比如10字节长)时,您会将数据包读入一个更大的缓冲区(1024字节)。然后要求Java字符串反序列化整个缓冲区,而不仅仅是10个字节

该方案(编码)不知道只处理前10个字节,因此它尝试解码整个1024个字节,然后你会在字符串上得到奇怪的字符,比如黑钻石,或者(在发送“hello”的过程中,你会在10个字节后加上一些其他数据),你会将前一个接收的字符混合到字符串中


使用write/readUTF将写入字节数组的长度以及数据,因此当您再次读取时,它将知道只需读取前10个字符(或有效的字符数)。

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

呼吁:

数组.fill(buf,(字节)0)

在服务器端,会将数组重置为零,因为Java中的数组是按引用传递的,而不是按值传递的(即,您对数组内容的引用与DatagramPacket的引用相同,因此您可以在不使用DatagramPacket方法的情况下对其进行修改)

话虽如此,序列化/反序列化数据的方式并不理想。最好在发送和接收缓冲区周围使用ByteArrayOutputStream和ByteArrayInputStream,然后在这些缓冲区周围使用DataOutputStream/DataInputStream。这将允许您在定义良好的环境中写入和读取字符串d格式,该格式可能存储字符串的长度,因此缓冲区中的任何剩余数据都将被忽略。以这种方式正确序列化/反序列化也将消除删除“黑钻石”字符的需要

如果您对这背后的原因感兴趣,那么这与您使用java.lang.String的默认序列化(getBytes()和new String(byte[])以及UDP数据包的填充方式有关。我将尝试将其归结为关键点:

Java的内部表示
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.