I am implementing a p2p application, in which nodes communicate by UDP packet. The packets read from InputStream are sometime not complete.
Here is my code:
...
protected String key;
protected Identifier messId; //Identifier hold a BigInteger
protected String range;
protected String concat;
....
public ReplicationMessage(DataInput in) throws IOException {
fromStream(in);
}
public void fromStream(DataInput in)
try {
super.fromStream(in);
int length=in.readInt();
byte[] data=new byte[length];
in.readFully(data);
concat = new String(data);
System.out.println("concat: "+concat);
messId = new Identifier(in);
} catch (IOException e) {
e.printStackTrace();
}
}
public void toStream(DataOutput out) {
try {
super.toStream(out);
byte[] data = concat.getBytes();
out.writeInt(data.length);
out.write(data);
messId.toStream(out);
} catch (IOException e) {
e.printStackTrace();
}
}
the read packet sometime is complete, for example
concat: 179136678282544:140737488355328
but sometime is not complete, for example
concat: 179136678282544
concat: 179136678282544
concat: 179136678282544
Can any one tell me what the problem is?
Many thanks
Here are the code for sending/receiving the UDP packet
for sending:
private void sendMessage(int comm, Message message, InetAddress ip, int port)
throws IOException {
ByteArrayOutputStream bout = new ByteArrayOutputStream();
DataOutputStream dout = new DataOutputStream(bout);
dout.writeInt(comm);
dout.writeByte(message.code());
message.toStream(dout);
dout.close();
byte[] data = bout.toByteArray();
if (data.length > DATAGRAM_BUFFER_SIZE) {
throw new IOException("Message too big, size="+data.length+
" bytes, max="+DATAGRAM_BUFFER_SIZE+" bytes");
}
DatagramPacket packet = new DatagramPacket(data, data.length, ip, port);
socket.send(packet);
}
For receiving the UDP packet
byte[] buffer = new byte[DATAGRAM_BUFFER_SIZE];
DatagramPacket packet = new DatagramPacket(buffer, buffer.length);
socket.receive(packet);
The sending node and receiving node belonging to one machine. the Buffer receiving packet is set to 10*1024 bytes which is much larger than packet length
Here is code for turning incoming datapacket into stream
ByteArrayInputStream bin = new ByteArrayInputStream(packet.getData(),
packet.getOffset(), packet.getLength());
DataInputStream din = new DataInputStream(bin);
int comm = din.readInt();
byte messCode = din.readByte();
Message message = factory.createMessage(messCode, din);
UDP does not deliver damaged or partial packets. Either you aren't sending all the data or you aren't unpacking it properly. You still haven't posted all the code concerned so it is not possible to say exactly where.
Related
I'm writing some networking code that writes basic packet constructs and sends them over a SocketChannel. I have the following relevant code:
in IOPacket (abstract implementation of the Packet interface, main thing getting sent over the channel):
//returns a bytebuffer representation of the object
#Override
public ByteBuffer write() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(this);
oos.flush();
oos.close();
ByteBuffer buf = ByteBuffer.wrap(baos.toByteArray());
buf.position(0);
return buf;
}
//returns a packet read from the given bytebuffer
public static Packet read(ByteBuffer buffer) throws IOException {
buffer.position(0);
System.out.println("[SQNetEvents] Remaining: " + buffer.remaining());
ByteArrayInputStream bais = new ByteArrayInputStream(buffer.array(), buffer.arrayOffset(), buffer.remaining());
ObjectInputStream ois = new ObjectInputStream(bais);
Packet packet = null;
try {
packet = (Packet) ois.readObject();
}
catch(Exception e) {
throw new IOException("Read object is not an implementation of Packet");
}
ois.close();
return packet;
}
in Connection (wrapper for a SocketChannel and all the behaviors needed to read, write, connect, etc.)
//sends a packet to the associated address
public void sendPacket(Packet packet) throws IOException {
System.out.println("In sendPacket()");
int length = getChannel().write(packet.write());
System.out.println("[SQNetEvents] Sent packet of length " + length + " to " + getChannel().getRemoteAddress().toString());
}
private class ListenThread extends Thread {
...
byte[] bytes = new byte[4096];
ByteBuffer buffer = ByteBuffer.wrap(bytes);
getChannel().read(buffer);
System.out.println("In Connection$ListenThread.run()");
Packet packet = IOPacket.read(buffer);
...
}
I get the StreamCorruptedException on the IOPacket.read(ByteBuffer) call, from the ObjectInputStream constructor call. I've been able to successfully send a string literal ("String1") converted to a byte[] and then wrapped in a ByteBuffer over the SocketChannel and read it without dealing with the ByteArray or Object streams, so I know that the issue must be somewhere in the stream code. The code on both ends is identical, and blocking is configured true for the channels. Any help is appreciated, thanks.
I'm finding a way to get an array, each component can have value between 0 and 255.
I'm using Android Studio and I received data from a socket. The following is my code.
try {
InetAddress IpAddress = InetAddress.getByName(remoteName);
myUpd_socket = new DatagramSocket(remotePort);
// Send connect message
String str = "connect request";
send_data = str.getBytes();
DatagramPacket send_packet = new DatagramPacket(send_data,str.length(), IpAddress, remotePort);
myUpd_socket.send(send_packet);
byte[] dataArray = new byte[1024];
DatagramPacket udp_packet = new DatagramPacket(dataArray,dataArray.length);
while (true) {
myUpd_socket.receive(udp_packet);
byte[] buff = new byte[udp_packet.getLength()];
System.arraycopy(dataArray,0,buff,0,buff.length);
}
} catch (SocketException e) {
e.printStackTrace();
} catch (UnknownHostException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
I need to get the same data from the transmitter. There is no negative value in original data. But when I checked the received data, the buff has a lot of negative values. I made something wrong here.
I'm a newbie of Java and android. So I'm really grateful if someone can help me.
Thank.
Why not to use:
RECEIVE
Socket socket = ...
DataInputStream in = new DataInputStream(socket.getInputStream());
int length = in.readInt(); // read length of incoming message
if(length>0) {
byte[] message = new byte[length];
in.readFully(message, 0, message.length); // read the message
}
SEND
byte[] message = ...
Socket socket = ...
DataOutputStream out = new DataOutputStream(socket.getOutputStream());
out.writeInt(message.length); // write length of the message
out.write(message); // write the message
bytes are signed in Java. If you want the unsigned value (as an int), use this: byteValue & 0xFF.
I'm trying to send an image that was taken by my camera in my application as a byte array. I convert the bitmap to byte array, then to Base64 string, then endcode that to a byte array and send. When I try to send it however I get an exception that my string is too long to send.
preparing to send through to socket:
if(currentChatPartner==null)
return;
ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
msg.compress(Bitmap.CompressFormat.PNG, 0, byteArrayOutputStream);
byte[] byteArray = byteArrayOutputStream .toByteArray();
String encoded = Base64.encodeToString(byteArray, 0);
sendThroughSocketImage(encoded, clientSocket, currentChatPartner.getIP(), currentChatPartner.getPort());
Sending through to socket:
byte[] decoded = null;
decoded = encoded.getBytes();
try
{
socket.send(new DatagramPacket(decoded, decoded.length, ip, port));
} catch (IOException e)
{
System.err.println("\nUnable to send message");
return false;
}
return true;
Sometimes it works, but most it doesn't :( Is there a reason for this?? Is there a way I could fix it or shorten the string that is to be sent??
By using DatagramPacket you're sending an UDP message that has a miximum length a little short of 64kb. Easy to get a message to long exception. You should send your data using TCP socket.
Plenty of examples online e.g.
http://systembash.com/content/a-simple-java-tcp-server-and-tcp-client/
With respect to your comments, an example of sending the byte array via client socket
InetAddress serverAddr = InetAddress.getByName(SERVER_IP);
socket = new Socket(serverAddr, SERVERPORT);
int start=0;
int len=decoded.length;
OutputStream out = socket.getOutputStream();
DataOutputStream dos = new DataOutputStream(out);
dos.write(array, start, len);
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.
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.