I'm a C++ programmer and have a need to set up some UDP communications between a java android app and the C++ server running on a PC.
I have structure that I need to receive on the PC that consists of the following:
int
int
float
Unfortunately I'm totally at a loss as to how I can do this with Java.
I need to create a DatagramPacket but the constructor only takes a byte array. Now with C++ this would be an easy cast from a struct to a char*. However casting like this is not possible with Java.
I've create a simple class that has the above fields in it. That seems to be fine. My remaining issue is how to turn that into a byte array. Can anyone help a Java noob on this front?
Cheers!
Edit: I've created a function in the class that does the following
public byte[] GetBytes() throws IOException
{
ByteArrayOutputStream byteOut = new ByteArrayOutputStream();
DataOutputStream dataOut = new DataOutputStream( byteOut );
dataOut.writeInt( Integer.reverseBytes( int1) );
dataOut.writeInt( Integer.reverseBytes( int2 ) );
dataOut.writeFloat( float1 );
return byteOut.toByteArray();
}
Is there a better way to do this?
I'd rather not use the google protocol buffer mentioned in Steve's answer because, while its interesting, it would require too many changes to other platform implementations that I'd really rather not do.
You can use Google protocol buffers as a language-independent way to serialize structures for transmission and receipt. Both Java and C++ are available out of the box, and Jon Skeet has written a production-ready C# implementation.
I see a couple of examples of Protobuf in use on Android, including this.
Another, maybe simpler approach comes from Javolution.struct: http://javolution.org/target/site/apidocs/javolution/io/Struct.html
public static class Student extends Struct {
public final Enum32<Gender> gender = new Enum32<Gender>(Gender.values());
public final UTF8String name = new UTF8String(64);
public final Date birth = inner(new Date());
public final Float32[] grades = array(new Float32[10]);
public final Reference32<Student> next = new Reference32<Student>();
}
class UDPMessage extends Struct {
Student student = inner(new Student());
...
}
...
public void run() {
byte[] bytes = new byte[1024];
DatagramPacket packet = new DatagramPacket(bytes, bytes.length);
UDPMessage message = new UDPMessage();
message.setByteBuffer(ByteBuffer.wrap(bytes), 0);
// packet and message are now two different views of the same data.
while (isListening) {
multicastSocket.receive(packet);
int xxx = message.xxx.get();
... // Process message fields directly.
}
}
Quite ugly piece of code, but still prettier than dealing directly with JNI buffers or already-mentioned Google protocol buffers.
Related
Hi and thanks in advance,
So I'm trying to take an array of JList items and convert them to a string array (which I think I've gotten right), and then I'm trying to send that string array over to my client who will then attempt to display them back into a JList on their side.
I've tried a few different things but none are working.
Here is my latest code attempt to send the string array over:
String[] FilesList = (String[]) lClient1Files.getSelectedValues();
FilesBuffer = FilesList.getBytes();
DatagramPacket DGPFilesResponse = new DatagramPacket(FilesBuffer,FilesBuffer.length, DGP.getAddress(), DGP.getPort());
SeederSocket.send(DGPFilesResponse);
The line: FilesBuffer = FilesList.getBytes(); is causing the issue because getBytes() isn't applicable here.
So my questions are:
1) How do I send the array of JList items(they are names) over to the client (it doesn't particularly have to be a string array), and
2) How would I receive the list on the clients side, so that I can use it?
Thank you.
One must make a binary format for the string array.
ByteArrayOutputStream baos = new ByteArrayOutputStream();
try (DataOutputStream dos = new DataOutputStream(baos)) {
dos.writeInt(filesList.length);
for (String files : filesList) {
dos.writeUTF(files);
}
}
byte[] bytes = baos.toByteArray();
This internally for a String writes first the length in bytes, and uses String.getBytes("UTF-8") so any string can be written.
Reading goes with the reversed input classes.
If you think of having many clients out there, maybe with different versions,
then add in the message a version number.
On the other side
ByteArrayInputStream bais = new ByteArrayInputStream(bytes);
try (DataInputStream dis = new DataInputStream(baos)) {
int stringsCount = dis.readInt();
String[] filesList = new String[stringsCount];
for (int i = 0; i < stringsCount; ++i) {
filesList[i] = dis.readUTF();
}
return filesList;
}
The UDP payload has to be a byte[]. You need to choose a way to encode your data into a byte[], such that it can be converted back at the receiving end.
So you need to write encode() and decode() so that unit tests like this work:
#Test
public void encodesAndDecodesStringArray() {
String[] strings = new String[] { "foo", "bar" };
byte[] encoded = Encoder.encode(strings);
String[] decoded = Encoder.decode(encoded);
assertThat(decoded, is(strings));
}
There are literally hundreds of encoding schemes you could choose from. Delimiter-separated, length-separated, JSON, XML, BSON, ASN.1 ... take a look at Wikipedia's List of data serialization formats.
A very simple option that might work for you is delimiter-separation:
public byte[] encode(String[] strings) {
return String.join(",", strings).getBytes(UTF_8);
}
public String[] decode(byte[] encodedArray) {
return new String(encodedArray, UTF_8).split(",");
}
But note that this very basic scheme fails if any of the input strings contains a "," (or whatever delimiter you choose). Pick a scheme that works for you.
Consider using JSON -- there are easy to use libraries to read and write JSON. Readable ASCII in network traces is often convenient. The space overhead is not that high. It's ready for arbitrarily complex hierarchical data structures.
Consider that if you change the structure of the data produced by your sender, the receiver must also change. If that matters, consider encoding a protocol version into what you send (it might be enough to just say "the first two bytes are the version", and always stick a 0x0001 in there to start with).
I use this in python:
test = zlib.compress(test, 1)
And now I want to use this in java, but I don't know how.
At the end I need to convert the result to a string...
I wait your help! thx
You need to be more specific in describing what you want to achieve.
Are you just seeking to decompress a zlib-compressed piece of data?
Or are you seeking for a way to exchange data between Python and Java, possibly by transferring it across a network?
For the former, it's basically sticking the compressed datastream in an Inflater (not Deflater!). Something like:
Inflater decompresser = new Inflater();
decompresser.setInput(data);
ByteArrayOutputStream bos = new ByteArrayOutputStream(data.length);
byte[] buffer = new byte[8192];
while (!decompresser.finished()) {
int size = decompresser.inflate(buffer);
bos.write(buffer, 0, size);
}
byte[] unzippeddata = bos.toByteArray();
decompresser.end();
For the latter, alex's answer already points to Pyro/Pyrolite.
You can try using a RPC to create a server.
https://github.com/irmen/Pyro4
And access that server using java.
https://github.com/irmen/Pyrolite
I'm using the DataOutputStream#WriteLong method in the java programming language to write a long to a stream, and I need to be able to read it from C# using the BinaryReader class from C# to try to read the data, the BinaryReader is connected to a NetworkStreamthat uses the TcpClient socket.
The java DataInputStream#ReadLong method is used to read the long value sent from the DataOutputStream in Java, however I'm trying to use the BinaryReader class to read this value.
Here's the method I have to read a long variable in C#
public static long ReadLong()
{
return binaryReader.ReadInt64();
}
However this is causing inconsistency, For example, I sent two longs through Java:
-8328681194717166436 || -5321661121193135183
and when I read them on C# I received the following results:
-7186504045004821876||-5642088012899080778
I can reproduce this as many times as I fun the application.
As you can read in the java documentation, WriteLong writes output "high bytes first", this is also known as Big Endian. Meanwhile, .NET BinaryReader reads data as Little Endian. We need something that reverses the bytes:
public class BigEndianBinaryReader : BinaryReader
{
private byte[] a16 = new byte[2];
private byte[] a32 = new byte[4];
private byte[] a64 = new byte[8];
public BigEndianBinaryReader(Stream stream) : base(stream) { }
public override int ReadInt32()
{
a32 = base.ReadBytes(4);
Array.Reverse(a32);
return BitConverter.ToInt32(a32, 0);
}
public Int16 ReadInt16BigEndian()
{
a16 = base.ReadBytes(2);
Array.Reverse(a16);
return BitConverter.ToInt16(a16, 0);
}
public Int64 ReadInt64BigEndian()
{
a64 = base.ReadBytes(8);
Array.Reverse(a64);
return BitConverter.ToInt64(a64, 0);
}
public UInt32 ReadUInt32BigEndian()
{
a32 = base.ReadBytes(4);
Array.Reverse(a32);
return BitConverter.ToUInt32(a32, 0);
}
}
I'm developing a client-server app where the server is C++ and the client is Java.
To communicate them, I use sockets. Now, I can transfer sucessfully Strings via the socket with this:
Client side:
public void serializeAndSendMessage(String msg) {
try {
os.write( msg.getBytes() );
os.flush();
}
catch ( Exception e ) {
e.printStackTrace();
}
}
Server side:
char recvbuf[DEFAULT_BUFLEN];
int iResult = recv(ClientSocket, recvbuf, recvbuflen, 0);
if (iResult > 0) {
printf("Bytes received: %d\n", iResult);
recvbuf[iResult] = 0; //set the end of the msg
How can I pass other data, like a Double or an Int? Is it possible to get a Double into a String, and then convert it back in the C++ server from char to Double?
Thanx.
You really want something like Google ProtocolBuffers which takes care of the serialization and deserialization of arbitrary data structures in a cross-platform and cross-language manner.
There are other libraries providing an alternative to ProtoBuf such as Apache thrift (and some of which add the networking which ProtoBuf is missing). That said, ProtoBuf is a fine choice as it is mature, used by Google themselves and has Java and C++ as core languages with first-tier support.
On the Java side, do something like this to get your bytes:
public static byte[] toByteArray(double value) {
byte[] bytes = new byte[8];
ByteBuffer.wrap(bytes).putDouble(value);
return bytes;
}
...
os.write(toByteArray(1.234));
os.flush()
Then, on the C++ side, you can get it back out like this:
double received;
memcpy(&received, recvbuf, sizeof(double))
hello i have a basic client-server system running using java sockets.
my problem is, that an object that i send from the client to the server does not contain the correct data after it has been sent once.
the first time i send it, it arrives with the correct values, but when i send it another time with different values, it still arrives at the server with the same values as the first time. it also happens if i send a completely different instance of that class. it always arrives with the data, which have been sent the very first time.
when i try this with other objects like java.lang.String it seems to work.
the problematic class looks like this:
public class Vector3f implements Serializable {
private static final long serialVersionUID = 2838034155614698213L;
public float x, y, z;
}
i use objectinputstream and objectoutputstream on both the server and the client to send and receive objects.
let me know, if you need any more information about the system.
thanks!
My guess is that you're changing the values of the fields and then retransmitting the same object. The ObjectOutputStream will notice that it's already sent the original object, and just send a reference the second time.
You could avoid this by calling reset() on the ObjectOutputStream - but I'd be tempted to just use separate instances anyway, possibly even making the class immutable. (Public mutable fields are almost never a good idea.)
The best way in case of serialization you should convert the object into a byte array object and then write into the socket.
// Serialize to a file
ObjectOutput out = new ObjectOutputStream(new FileOutputStream("filename.ser"));
out.writeObject(object);
out.close();
// Serialize to a byte array
ByteArrayOutputStream bos = new ByteArrayOutputStream() ;
out = new ObjectOutputStream(bos) ;
out.writeObject(object);
out.close();
// Get the bytes of the serialized object
byte[] buf = bos.toByteArray();
// Deserialize from a file
File file = new File("filename.ser");
ObjectInputStream in = new ObjectInputStream(new FileInputStream(file));
// Deserialize the object
Object obj = (Object) in.readObject();
in.close();
// Get some byte array data
byte[] bytes = getBytesFromFile(file);
// see Reading a File into a Byte Array for the implementation of this method
// Deserialize from a byte array
in = new ObjectInputStream(new ByteArrayInputStream(bytes));
in.close();