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

How to reinitialize the buffer of a packet?(如何重新初始化数据包的缓冲区?)

本文介绍了如何重新初始化数据包的缓冲区?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

I am having some problem while I am sending UDP packets from netcat or my client to my UDP server which listens for broadcast UDP packets. The problem is that I can't reinitilize the buffer of socket.receive(packet); And as you check my console output you will see that the packets are sent or received twice or even more times and most annoying of all is that when I send a packet with greater length first, the next one which is smaller consists part of the previous! (Problems are marked with HERE on the console output) My Client and Server are located on the same LAN.

Client code:

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 code:

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;
    }

Console:

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.

Any help will be highly appreciated! :)

PS. there might be some outputs in the console like Done/Sent/Connecting/Receiving that are not added to my sample code but all the Received:/BUFFER_packet/_socket/From are present.

解决方案

you don't need to reinitialize the buffer in the packet, you just need to reset the contents of the buffer to whatever they were initially (i.e. you need to fill the receiving array with zeroes).

A call of:

Arrays.fill(buf,(byte)0);

on the server side will reset the array to zeroes since arrays in Java are pass-by-reference not pass-by-value (i.e. the reference you have to the array contents is the same as the reference that the DatagramPacket has, so you can modify it without going through DatagramPacket methods).

Having said that the way you are serialising/deserialising the data isn't ideal. You would be better off using a ByteArrayOutputStream and ByteArrayInputStream wrapped around the sending and receiving buffers, and then a DataOutputStream / DataInputStream wrapped around those. These would allow you to write and read the string in a well defined format which would likely store the length of the string so that any remaining data on the buffer would be ignored anyway. Properly serialising / deserialising in this way would also remove the need to strip the 'black diamond' character.

DataOutputStream.writeUTF

If you're interested in the reasoning behind this it is to do with your use of java.lang.String's default serialisation (getBytes() and new String(byte[])) and how the UDP packet is being populated. I'll try and boil it down to the crucial bits:

Java's internal representation for String objects is not a byte array - its a char array. Java chars are not the same as bytes - one char is actually 2 bytes because it needs to be able to represent more than just the latin alphabet (acbd...), it needs to support other characters from other languages/cultures like stuff from Cyrillic, Kanji etc and one byte isn't enough (one byte gets you 256 possibilities, 2 bytes gets you 65536 possibilities).

As a result when you call getBytes() Java has to use some 'scheme' (encoding) to turn that character array into a byte array (serialisation). The details of that don't matter too much, but when you send the first chunk of bytes, (lets say its 10 bytes long) you read the packet into a much bigger buffer (1024 bytes). Then you ask Java String to deserialise that entire buffer, not just the 10 bytes.

The scheme (encoding) doesn't know to only deal with the first 10 bytes so it tries to decode the whole 1024 bytes, and then you get weird characters on your string like the black diamond, or (where you've put some other data after the 10 bytes by sending 'hello') you get characters from the previous receive mixed into your string.

Using write/readUTF will write the length of the byte array as well as the data, so when you read it again it will know it only has to read the first 10 characters (or however many are valid).

这篇关于如何重新初始化数据包的缓冲区?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!

本文标题为:如何重新初始化数据包的缓冲区?

基础教程推荐