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.
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).
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.
Related
I'm using UDP to send a sequence code to my receiver, and I want to now measure the delay between when the packet is being sent and received. The only problem is I can't figure out how to read my delay from the packet after the sequence number.
Sender
byte [] packetToSend = new byte[520];
int seqNo = 0;
while (seqNo < 100) {
try {
//Read in a audio data from the recorder
byte[] buffer = recorder.getBlock();
ByteBuffer header = ByteBuffer.allocate(524);
long sentMillis = System.currentTimeMillis();
header.put(buffer);
header.putInt(seqNo);
header.putLong(sentMillis);
//Make a DatagramPacket from it, with client address and port
number
DatagramPacket packet = new DatagramPacketheader.array(),
header.array().length, clientIP, PORT);
//Send it
sending_socket.send(packet);
Receiver:
try {
byte[] buffer = new byte[520];
DatagramPacket packet = new DatagramPacket(buffer, 0, 520);
receiving_socket.receive(packet);
// int seqNo = ByteBuffer.wrap(buffer).getInt(512);
long sentMillis = ByteBuffer.wrap(buffer).getLong(508);
long receivedMillis = System.currentTimeMillis();
long delay = receivedMillis - sentMillis;
System.out.println(delay);
So far I get numbers like 1498894798102, I'm guessing I'm reading from the wrong index in my ByteBuffer.wrap line.
Allocate a larger ByteBuffer and keep using the various get() (and put() when sending) methods. Lose all the System.arraycopy() calls.
At Client I chunks buffer with Arrays.CopyofRange() from 1 FileEvent object:
byte[] data = outputStream.toByteArray();
for (int i = 0; i <= data.length; i += incommingData.length) {
byte[] forSent = new byte[0x3ff];
forSent = Arrays.copyOfRange(data, i, i + 1023);
DatagramPacket sendPacket = new DatagramPacket(forSent, forSent.length, ipAddress, PORT);
socket.send(sendPacket);
System.out.println("File sent from client : " + i);
}
String done = "Done";
DatagramPacket stringDone = new DatagramPacket(done.getBytes(), done.length(), ipAddress, PORT);
socket.send(stringDone);
And at Server:
do {
byte[] incomingData = new byte[1024 * 1000 * 50];
incomingPacket = new DatagramPacket(incomingData, incomingData.length);
socket.receive(incomingPacket);
byte[] data ;
data = incomingPacket.getData();
result = Arrays.copyOfRange(data, 0, data.length);
System.out.println("Da nhan:" + result.length);
message = new String(incomingPacket.getData(), 0, incomingPacket.getLength());
System.out.println(message);
} while ( !message.equals("Done"));
after send all buffer, i send 1 String "done". And at Server i check end of file by String "done". But it not work, via UDP, how can i detect end of file transfer. Thanks!
UDP is an unreliable protocol. Messages may be lost without either the sender or receiver being notified. This is particularly likely if the sender sends a large number of messages in quick succession ... as your code is liable to do.
So I suspect that what is happening in your case is that the message that contains the "done" string is being lost.
There is no easy cure for this using UDP. It would entail implementing flow control, message loss detection and retransmission ... at the application protocol level. This is technically possible, but it is simpler to either switch to TCP, or look for an alternative reliable protocol.
I am creating a class object to byte array see here, and passing this array to DatagramPacket to send at destination by means of UDP communication. At the destination side there is C application running and I am getting wired character. I damn sure this related to byte order. Because I am able to convert struct of C into class while receiving packet, but not able to do vice versa. Following is the sudo - code:
// recieving part
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet)
UdpPacket udp = new UdpPacket(buffer);
class UdpPacket implements Serializable
{
int a;
byte []message = new byte[10];
public UdpPacket(byte[] data)
{
ByteBuffer bb = ByteBuffer.wrap(data);
bb.order(ByteOrder.nativeOrder());
a = bb.getInt();
bb.get(message);
}
}
// sending ..
Converting UdpPacket object to byte array as per above mentioned link, but at the destination there is always junk values
DatagramSocket clientSocket = new DatagramSocket();
byte [] respoBytes = ObjectSerializer.serializeObject(udpPacket);
ByteBuffer bb = ByteBuffer.wrap(respoBytes);
byte []test = new byte[1116];
bb.order(ByteOrder.nativeOrder()); // tried all the orders here
bb.get(test);
DatagramPacket sendPacket = new DatagramPacket(test,test.length,client, Integer.parseInt(TxtFdPort.getText().toString()));
clientSocket.send(sendPacket)
;
The most obvious problem is that ignore the length of data read.
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
ByteBuffer bb = ByteBuffer.wrap(packet.getData(), 0, packet.getLength());
// you need to know the endianess, not hope it's the same.
bb.order(ByteOrder.LITTLE_ENDIAN);
int a = bb.getInt(); // asuming there is at least 4 bytes.
byte[] message = new byte[bb.remaining()];
bb.get(message);
For Sending I would just do this.
int port = Integer.parseInt(TxtFdPort.getText().toString());
byte [] respoBytes = ObjectSerializer.serializeObject(udpPacket);
DatagramPacket sendPacket = new DatagramPacket(respoBytes, respoBytes.length, client, port);
clientSocket.send(sendPacket);
Print out the sequence of bytes before sending, and after receiving. This will tell you if they are identical. Then inspect the bytes corresponding to e.g. an int, and you will immediately see if they are in the correct order or not.
It's hard to be more specific, since you're not showing the sending code.
I try to send a integer array via datagram socket. What is the best way to accomplish this.
MY sending code is:
public void sendObj(Object obj) {
try{
byteArr = new ByteArrayOutputStream();
objOut = new ObjectOutputStream(byteArr);
objOut.writeObject(obj);
byte[] b = byteArr.toByteArray();
DatagramPacket dgram = new DatagramPacket(b, b.length, InetAddress.getByName("230.0.0.1"), 4446); // multicast
socket.send(dgram);
System.out.println("Package is sent!");
}catch(Exception e){
System.out.println(e.getMessage());
e.printStackTrace();
}
}
And receiving code is:
byte[] b = new byte[65535];
ByteArrayInputStream b_in = new ByteArrayInputStream(b);
DatagramPacket dgram = new DatagramPacket(b, b.length);
socket.receive(dgram); // blocks
ObjectInputStream o_in = new ObjectInputStream(b_in);
Object o = o_in.readObject();
dgram.setLength(b.length); // must reset length field!
b_in.reset(); //
However when I receive it gives StreamException for unknown header value 00000
We solved that problem by a basic changes. We used a string like "100200300..." to symbolize the array by separating element with two 0' so in that way we did not use ArrayList.
You have to use getbyte() method to get the bytes and the put these bytes into a datagram packet instance and then pass it to the client or send it...
Use
arrayName.getBytes(); /// save this into some byte[] temp = new byte[size];
and make a datagram packet and put its argumnets and send it
I'm implementing a client/server application using UDP transmissions. Here is my part of my code :
Client :
InetAddress serverAddress = ...
int serverPort = ...
DatagramSocket socket = new DatagramSocket(9999);
...
String message = "<HELLO>";
byte[] outbuffer = new byte[1000];
outbuffer = message.getBytes();
DatagramPacket packet = new DatagramPacket(outbuffer, outbuffer.length, serverAddress, serverPort);
socket.send(this.packet);
Server :
DatagramSocket serverSocket = new DatagramSocket(9876);
...
byte[] inbuffer = new byte[1000];
DatagramPacket packet = new DatagramPacket(inbuffer, inbuffer.length);
serverSocket.receive(packet);
String response = new String(packet.getData(), 0, packet.getLength());
System.out.println(response);
if("<HELLO>".equals(response)){
System.out.println("OK");
} else {
System.out.println("ERROR");
}
My problem is the following: if I print the response String on the client side that is comming from the client, everything looks fine ("").
But for some reasons when I trie to compare the response coming from the server using .equals or a RegExp it fails !
May be it's related to String encoding but I don't know where and why it fails. Both client and server are running on the same host right now, so it might not be related to JVM differences.
You're currently using the platform default encoding to both encode and decode strings. You should absolutely not do that. Specify the encoding both in the getBytes() call and the constructor call, e.g.
byte[] outBuffer = message.getBytes("UTF-8");
Also note that your current code creates a byte array of length 1000 and then immediately throws it away:
byte[] outbuffer = new byte[1000];
// Byte array created on previous line is now useless!
outbuffer = message.getBytes();
... don't do that.
We can't really tell much more from the code you've given us - if you could produce short but complete programs demonstrating the problem, that would really help.
For debugging, I would suggest you log the contents of the datagram packet you receive, while still in binary. Presumably it's not what you expected, but that doesn't help to show what it was.
EDIT: Here's a pair of short but complete programs which do work:
import java.net.*;
public class Server {
public static void main(String[] args) throws Exception{
DatagramSocket socket = new DatagramSocket(9999);
byte[] inbuffer = new byte[1000];
DatagramPacket packet = new DatagramPacket(inbuffer, inbuffer.length);
socket.receive(packet);
String response = new String(packet.getData(), 0,
packet.getLength(), "UTF-8");
System.out.println(response);
if("<HELLO>".equals(response)){
System.out.println("OK");
} else {
System.out.println("ERROR");
}
}
}
// Client.java
import java.net.*;
public class Client {
public static void main(String[] args) throws Exception {
DatagramSocket socket = new DatagramSocket();
byte[] output = "<HELLO>".getBytes("UTF-8");
DatagramPacket packet = new DatagramPacket(output, output.length,
InetAddress.getLocalHost(),
9999);
socket.send(packet);
}
}
You have a variable response and reponse.
I am assuming that they won't be the same and your test should fail.
This is the sort of thing you should be able to see in a debugger.