11

I am trying to send a serialized object from a server process to a client process in Java using UDP. The problem is that the client is being blocked on the receive method. Can someone help?!

here is the server code for sending the object:

  ClientModel C1= new ClientModel(100,"Noor","Noor",38,38,"asd");
  ByteArrayOutputStream baos = new ByteArrayOutputStream();
  ObjectOutputStream oos = new ObjectOutputStream(baos);
  oos.writeObject(C1);
  oos.flush();
  byte[] Buf= baos.toByteArray();
  packet = new DatagramPacket(Buf, Buf.length, client, port);
  socket.send(packet);

and here is the client code for receiving the object:

byte[] buffer = new byte[100000];
packet = new DatagramPacket(buffer, buffer.length );
socket.receive(packet);
System.out.println("packet received");

I just want to receive the object to be able to reconstruct but I cannot receive the packet itself.

tshepang
  • 12,111
  • 21
  • 91
  • 136
Noor
  • 19,638
  • 38
  • 136
  • 254

2 Answers2

14

I dont know what you want to accomplish in the end, but working with UDP is not so easy... the main reason is in the Description of the DatagramPacket Object:

Datagram packets are used to implement a connectionless packet delivery service. Each message is routed from one machine to another based solely on information contained within that packet. Multiple packets sent from one machine to another might be routed differently, and might arrive in any order. Packet delivery is not guaranteed.

A good tutorial when working with udp is http://download.oracle.com/javase/tutorial/networking/datagrams/clientServer.html

About your blocking:

Receives a datagram packet from this socket. When this method returns, the DatagramPacket's buffer is filled with the data received. The datagram packet also contains the sender's IP address, and the port number on the sender's machine.

This method blocks until a datagram is received. The length field of the datagram packet object contains the length of the received message. If the message is longer than the packet's length, the message is truncated.

I didnt really test it, but I am pretty sure - based on the description - that the datagramsocket.reseive function will block until the packet is filled (in your case until 100000 bytes are received).

I would suggest you start with a datagrampacket with a fixed known length, where you transmit the size of the actual payload. Something like:

public static void main(String[] args) {
    ClientModel c1 = new ClientModel ();
    c1.data = 123;
    c1.name = "test";

    try {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos);
      oos.writeObject(c1);
      oos.flush();
      // get the byte array of the object
      byte[] Buf= baos.toByteArray();

      int number = Buf.length;;
      byte[] data = new byte[4];

      // int -> byte[]
      for (int i = 0; i < 4; ++i) {
          int shift = i << 3; // i * 8
          data[3-i] = (byte)((number & (0xff << shift)) >>> shift);
      }

      DatagramSocket socket = new DatagramSocket(1233);
      InetAddress client = InetAddress.getByName("localhost");
      DatagramPacket packet = new DatagramPacket(data, 4, client, 1234);
      socket.send(packet);

      // now send the payload
      packet = new DatagramPacket(Buf, Buf.length, client, 1234);
      socket.send(packet);

      System.out.println("DONE SENDING");
    } catch(Exception e) {
        e.printStackTrace();
    }
}

On the other side you now KNOW your sizes:

public static void main(String[] args) {
    try {
      DatagramSocket socket = new DatagramSocket(1234);

      byte[] data = new byte[4];
      DatagramPacket packet = new DatagramPacket(data, data.length );
      socket.receive(packet);

      int len = 0;
      // byte[] -> int
      for (int i = 0; i < 4; ++i) {
          len |= (data[3-i] & 0xff) << (i << 3);
      }

      // now we know the length of the payload
      byte[] buffer = new byte[len];
      packet = new DatagramPacket(buffer, buffer.length );
      socket.receive(packet);

        ByteArrayInputStream baos = new ByteArrayInputStream(buffer);
      ObjectInputStream oos = new ObjectInputStream(baos);
      ClientModel c1 = (ClientModel)oos.readObject();
      c1.print();
    } catch(Exception e) {
        e.printStackTrace();
    }
}

The CientModel clas sI used:

public class ClientModel implements Serializable{
    private static final long serialVersionUID = -4507489610617393544L;

    String name = "";
    int data = 1;

    void print() {
        System.out.println(data +": " + name);
    }
}

I tested this code and it works just fine. Hope that helps (I got the byte-To-int and around from http://www.tutorials.de/java/228129-konvertierung-von-integer-byte-array.html)

Edit: As stated in the comments, it is often a very bad idea to use UDP, mainly, because you do not know if your packets are received in the correct order, or even at all. UDP does NOT guarantee that. I didn't do too much udp programming, but the only part you can rely on (if I understood correctly) is, that if you get a packet and it fits within the datagram (65,527 bytes - see https://en.wikipedia.org/wiki/User_Datagram_Protocol) it will contain the whole thing. So if you do not care about the order in which the message come and your object fits in the datagram, you should be fine.

Edit2: As for the code: do not use it as is. it is only an example, ind UDP you should only have one type of packet, and this with a known size. that way you do not need to send the "size". If you use the code as shown above, and one packet is dropped, the next packet will be the wrong size (i.e. the first packet is dropped, suddenly you are checking the first bytes of the payload to get the size).

Niko
  • 6,133
  • 2
  • 37
  • 49
  • I'm not tested either, but from description of C recvfrom(2), it's evident that reading from UDP connection can return as soon as whole datagram is read and actual number of read bytes is returned (in case of JDK it will be in DatagramPacket.length field). I suggest writing simple Java echo client/server to test behavior. – Victor Sorokin Oct 22 '10 at 14:58
  • Datagrams can arrive out of order or never at all. You might have to acknowledge the initial packet to confirm the next expected size and then ack the payload packet – user12345613 Mar 16 '12 at 23:40
  • So you send the length before you send the actual payload? How does this change the situation? The syntax in both sourcecode excerpts for transmitting the payload remain the same. I am confused – Igbanam Mar 14 '13 at 08:14
  • of course! the only difference in your implementations come in the receiving end. You deserialize the object received using a byte input stream. This seems to work for me too and is the correct way of doing things. Thanks! – Igbanam Mar 14 '13 at 09:32
  • Using UDP is a good idea, but that depends on your application needs. If you need to broadcast that Object, so, the way to go is UDP as TCP doesn´t support Broadcasting – Daniel Feb 28 '14 at 20:03
2

I didnt really test it, but I am pretty sure - based on the description - that the datagramsocket.reseive function will block until the packet is filled (in your case until 100000 bytes are received).

This is wrong. The receive function will block until a datagram is received, which can be smaller than the buffer size (and usually will be). The method packet.getLength() will tell you how big it was.

teu
  • 809
  • 11
  • 13