3

I have a server-client application that is using a datagram socket to exchange messages. I have initially set the buffer size to be 1024 bytes because I dont know the length of the messages. When I send something that is shorter than 1024 bytes I get the rest of my string displayed as some weird characters (null characters or I am not sure how they are called). Here is a screen: string buffer null characters

Client code: byte[] buf = ("This is another packet.\n").getBytes(); DatagramPacket packet = new DatagramPacket(buf, buf.length, inetAddress, serverport); socket.send(packet)

Server code: byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive(packet);

user207421
  • 305,947
  • 44
  • 307
  • 483
Miky
  • 942
  • 2
  • 14
  • 29
  • You're talking about the size of a `BufferedReader` or something else? – LuxuryMode Nov 22 '11 at 15:14
  • no, I am not reading a file but a packet. Server code looks like that: `byte[] buf = new byte[1024]; DatagramPacket packet = new DatagramPacket(buf, buf.length); socket.receive(packet);` – Miky Nov 22 '11 at 15:20
  • 2
    IMO, it's better to update your OP than to post your code in comments. – Bhesh Gurung Nov 22 '11 at 15:29
  • Can you also send the length of the data? For example, in my application, I'm doing something similar. I know that the largest message is n bytes. I prepend a 2 byte short to the message so that the first 2 bytes say how many of the next n bytes are relevant. – Thomas Owens Nov 22 '11 at 15:34
  • @ThomasOwens and what happens when the packet's bytes are more than the maximum value displayed by a short? I hoped for a generic method that already exists in the Java public API. Something that would work like `socket.receive(packet); String message = (new String(packet)).getridofannoyingcharacters();` or a method that I could create myself and removing those characters from the String, but I dont know their ascii code. – Miky Nov 22 '11 at 15:44
  • 1
    @MikeMokkas The maximum size for a UDP datagram is 65k. A short gives a range of 0 to 32 767 (simply because you can't have a negative data size), and we don't have any messages larger than 32767. If you do, then append a 4 byte integer. That would still leave you with 64k - 4 bytes of usable data transfer space. – Thomas Owens Nov 22 '11 at 15:49
  • 1
    @ThomasOwens, You could use a `char` as an unsigned 16-bit value. – Peter Lawrey Nov 22 '11 at 16:29
  • 1
    @Thomas: You are mixing up a lot of smattering here. First of all and most important is that Java DatagramPackets know their length, so there is absolutely no need to encode it in the packet's payload. Except for that, there is no reason to consider the "signedness" of Java data types to decide if you would have needed 2 bytes (up to 64kB) or 4 bytes (up to 4TB) to encode the packet length. And at last, the maximum payload length for a UDP datagram (the Java DatagramSocket is BTW not restricted to UDP) is neither 65 nor 64kB, but limited by the IP packet length restriction. – jarnbjo Nov 22 '11 at 18:56
  • @jarnbjo A DatagramPacket does not know its length. It always fills the provided buffer. Also, if a UDP datagram is larger than the MTU, it will be broken into several IP packets under the hood. Failure to deliver any IP packet results in a lost UDP datagram. Try to send a byte array of over 64k (it is 64, not 65k - my mistake) - it can't happen. See [one of my questions](http://stackoverflow.com/questions/7794401/in-java-how-do-i-deal-with-udp-messages-that-are-greater-than-the-maximum-udp-d). – Thomas Owens Nov 22 '11 at 19:04
  • @Thomas: DatagramPacket is a Java class, which abstracts any packet based communication protocol. It makes no sense to claim what DatagramPacket can and can't without relating it to a specific implementation. In case of UDP, the packet length is well known and encoded in bits 32 to 47 in the UDP header. Since the header length is included in this 16 bit field, the max payload length of a pure UDP packet is 65527 bytes. When using UDP over IP (the normal case), the payload length is limited further to 65515 bytes (IPv4) or 65487 bytes (IPv6) due to IP packet length restrictions. – jarnbjo Nov 23 '11 at 00:11
  • @Thomas Owens (1) The maximum size of a UDP payload is 65507 bytes, not 65k or 64k, and in practice it is limited to < 1500 bytes by the path MTU and the implicit non-fragmentation requirement. (2) A received `DatagramPacket` *does* know its length: it is available to UDP in the UDP packet, and to Java via the result of `recv()`, and to Java code via the `DatagramPacket.getLength()` method. (3) It doesn't 'fill the provided buffer'. – user207421 Nov 23 '11 at 00:13
  • @jarnbjo 65527 is not correct, IP uses 20 bytes of that. IPv6 jumbograms can be *much* larger. – user207421 Nov 23 '11 at 00:16

4 Answers4

10
socket.receive(packet);
byte[] data = new byte[packet.getLength()];
System.arraycopy(packet.getData(), packet.getOffset(), data, 0, packet.getLength());
Afrig Aminuddin
  • 772
  • 1
  • 9
  • 22
4

DatagramPacket.getLength() returns the actual length of the received packet. Unless you created the packet with a non-zero offet, that means the data is at {0..getLength()-1}.

Note that this means the original length you created the DatagramPacket with is lost, which in turn implies that you must either use a new DatagramPacket per receive, or at least re-initalize its data buffer via setData(). Otherwise the DatagramPacket will keep shrinking to the size of the smallest packet received.

user207421
  • 305,947
  • 44
  • 307
  • 483
1

You have to check packet.getOffset() to find where in the buffer the received data starts and packet.getLength() to get the length of the data (in number of bytes).

You should also consider that if the received packet is too large to fit in the provided buffer (in your case >1024 bytes), the extra data is simply discarded. Unless you have to be very careful on memory usage, you should use a larger buffer to make sure that the entire packet will fit. In case of UDP, the maximum packet size is 64kB.

jarnbjo
  • 33,923
  • 7
  • 70
  • 94
  • I know where it starts, it always starts from the first byte but I dont know on which byte my string **ends** and then those null characters start to appear. – Miky Nov 22 '11 at 15:48
1

Ok so I came up with a solution that worked for me:

    public String getRidOfAnnoyingChar(DatagramPacket packet){
        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;
    }

EDIT: There exists a better solution using ByteArrayOutputStream which can be found here: How to reinitialize the buffer of a packet?

Community
  • 1
  • 1
Miky
  • 942
  • 2
  • 14
  • 29