Sockets java sending object to c++ server - java

I am using sockets and I have one c++ server and one java client.
My c++ server is waiting for a c++ struct like this one:
struct Testing{
double a;
double b;
double c;
};
And my server code looks like this:
int recvStruct(int fd)
{
struct Testing test;
int ibytes = sizeof(struct Testing);
if (ibytes!=read(fd,&test,ibytes))
{
printf("Error read\r\n");
return 0;
}
else
{
dosomething();
Then in my java client, i created one class:
public class Testing{
double a,b,c;
public Testing(double a, double b, double c){
this.a = a;
this.b = b;
this.c = c;
}
}
and for the last, my client java code is this one:
try {
Socket socket = new Socket("ipaddress", 12345);
OutputStream os= socket.getOutputStream();
Testing object = new Object(1,2,3);
os.writeObject(object);
socket.close();
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
/* e.printStackTrace();
response = "UnknownHostException: " + e.toString();*/
} catch (IOException e) {
// TODO Auto-generated catch block
/*e.printStackTrace();
response = "IOException: " + e.toString();*/
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
// TODO Auto-generated catch block
/* e.printStackTrace();*/
}
}
My problem is that i always get that error read message.
Thank you.

Your code just assumes that your C++ Testing class consists of the exact same number of bytes, in the exact same order, as the result of Java's OutputStream.writeObject function.
OutputStream.writeObject isn't even a function to serialise the component bytes of its members; it does much more than that:
The class of the object, the signature of the class, and the values of the non-transient and non-static fields of the class and all of its supertypes are written.
In conclusion, given that there is no relationship between the two whatsoever, success is extremely unlikely.
Instead, serialise to your own known format, then deserialise into the language structure you require. Research the bolded terms for more information.

If your struct is a sequence of 3 double, assuming in C++ a double is 8 byte as in Java, you should send a sequence of 24 bytes.
To convert each double I would use:
long bits = Double.doubleToLongBits(someDouble);
byte[] bytes = new byte[8];
for(int offset = 0, shift = 7*8;
offset < bytes.length;
offset++, shift-=8)
bytes[offset] = (byte)(bits >>> shift);

Related

Setting raw data/byte array as a source of an ImageView

I have a Client-Server system where server is written in cpp and the client is written is Java (Android application).
The server reads an image from a local directory as an ifstream using read method.
The reading process is done inside a loop, where the program reads parts of the image every time. Every time a part of the image is read, it's sent over a socket to the client that collects all the piece inside a byteBuffer and when all the bytes of the image are transfered to the client, the client attempts to turn that array of bytes (after using byteBuffer.array() method) into a Bitmap.
This is where the problem begins - I've tried a few methods but it seems that I'm unable to turn this array of bytes into a Bitmap.
From what I understood, this byte array is probably a raw representation of the image, which can't be decodded using methods like BitmapFactory.decodeByteArray() since it wasn't encoded in the first place.
Ultimately, my question is - how can I proccess this array of bytes so that I'll be able to set the image as a source to an ImageView?
Note: I've already made sure that all the data is sent over the socket correctly and the pieces are collected in the right order.
Client code:
byte[] image_bytes
byte[] response_bytes;
private void receive_image ( final String protocol, final int image_size, final int buffer_size)
{
if (image_size <= 0 || buffer_size <= 0)
return;
Thread image_receiver = new Thread(new Runnable() {
#Override
public void run() {
ByteBuffer byteBuffer = ByteBuffer.allocate(image_size);
byte[] buffer = new byte[buffer_size];
int bytesReadSum = 0;
try {
while (bytesReadSum != image_size) {
activeReader.read(buffer);
String message = new String(buffer);
if (TextUtils.substring(message, 0, 5len_of_protocol_number).equals(protocol)) {
int bytesToRead = Integer.parseInt(TextUtils.substring(message,
len_of_protocol_number,
len_of_protocol_number + len_of_data_len));
byteBuffer.put(Arrays.copyOfRange(buffer,
len_of_protocol_number + len_of_data_len,
bytesToRead + len_of_protocol_number + len_of_data_len));
bytesReadSum += bytesToRead;
} else {
response_bytes = null;
break;
}
}
if (bytesReadSum == image_size) {
image_bytes = byteBuffer.array();
if (image_bytes.length > 0)
response_bytes = image_bytes;
else
response_bytes = null;
}
} catch (IOException e) {
response_bytes = null;
}
}
});
image_receiver.start();
try {
image_receiver.join();
} catch (InterruptedException e) {
response_bytes = null;
}
if (response_bytes != null)
{
final ImageView imageIV = (ImageView) findViewById(R.id.imageIV);
File image_file = new File(Environment.getExternalStorageDirectory(), "image_file_jpg");
try
{
FileOutputStream stream = new FileOutputStream(image_file);
stream.write(image_bytes);
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
catch (IOException e)
{
e.printStackTrace();
}
//Here the method returns null
final Bitmap image_bitmap = BitmapFactory.decodeFile(image_file.getAbsolutePath());
main.this.runOnUiThread(new Runnable() {
#Override
public void run() {
imageIV.setImageBitmap(image_bitmap);
imageIV.invalidate();
}
}
}
}
Whenever you exchange data between two machines of different architectures via sockets you need to know the Endianness (big-endian/little-endian) of each machine. If different, you will need to convert bytes to correct the data. Perhaps that's your issue. Here's a link with sample code: Converting Little Endian to Big Endian. You should be able to easily find more articles explaining the concept.
It turned out that something was wrong with my sending protocol.
After patching it up a bit it actually worked.
Thanks for the help.

Parcel through socket

I'm trying to send a parcel through a socket to an android application. The client is in libbinder(c++) and the server is an android application that will have to rebuild the parcel. I have been looking for a solution for some time but I don't know how to serialize the parcel and then re-build it on the server side. Any ideas on how this can be done?
Thanks
The part of code where I handle the data
Client
Parcel parc = Parcel();
double three = 5.5;
parc.writeDouble(three);
unsigned char b[sizeof(parc)];
std::memcpy(b, &parc, sizeof(parc));
Then I send like this
send(client, b, sizeof(b), 0);
Server
private int count
private InputStream in = null;
try {
in = socket.getInputStream();
} catch (IOException e) {
e.printStackTrace();
}
try {
count = in.read(bytes);
}catch (IOException e) {
e.printStackTrace();
}
Parcel parcel = Parcel.obtain();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
double d = parcel.readDouble();
Log.v("----double---", "double" + d);
A good example can be found here.
In general, you will need to make sure that you have the classes available to reconstruct (create from parcel) the objects.

Deserializing multiple objects from file without using while(true)

I have a block of code, that deserializes multiple objects from file. How can i avoid using a while(true)?
ObjectInputStream in = new ObjectInputStream(new FileInputStream(
filename));
while (true) {
try {
MyObject o = (MyObject) in.readObject();
// Do something with the object
} catch (EOFException e) {
break;
}
}
in.close();
You should write either a collection (with a size), or a put a marker before each object:
try {
for (;in.readBoolean();) {
MyObject o = (MyObject) in.readObject();
}
} catch (EOFException e) {
// ...
}
When you write your object, write a boolean just before (it will however take 1 byte if I do remember well that part):
for (MyObject o : iterable) {
out.writeBoolean(true);
out.writeObject(o);
}
out.writeBoolean(false);
If iterable is a collection or map, you can use default serialization:
out.writeObject(iterable); // default collection serialization
Beside, don't catch an exception for each item, catch it globally (especially EOFException!): it is better for performance reasons.
I don't know if you work with Java 7, but your code + my for loop can be written like this:
try (ObjectInputStream in = new ObjectInputStream(new FileInputStream( filename))) {
for (;in.readBoolean();) {
MyObject o = (MyObject) in.readObject();
}
} catch (EOFException e) {
// ...
}
// no need to close, the try-with-resources do the job for you.
How can i avoid using a while(true)?
You can't.
More to the point, why do you think you want to?
This is a classic example of the tail wagging the dog. EOFException is thrown to indicate end of stream. Ergo you have to catch it, and ergo you have to loop until it is thrown, ergo you have to use while (true) or one of its cognates.
The exception thought police would have you prepend an object count, taking the curious position that external data structures should be designed to suit the coder's phobias, and overlooking that you may not know it in advance, or may need to change your mind, or may need to exit prematurely; or would have you write a null as an end-of-stream marker, overlooking that it prevents the use of null for any other purpose; and in both cases overlooking the fact that the API is already designed to throw EOFException, and already works the way it already works, so you already have to code accordingly.
The code that I'm proposing let you to serialize and deserialize multiple objects really easily without having any problems and avoiding the awful (in my opinion) while true:
public class EntityClass implements Serializable{
private int intVal;
private String stringVal;
public EntityClass(int intVal, String stringVal) {
this.intVal = intVal;
this.stringVal = stringVal;
}
#Override
public String toString() {
return "EntityClass{" +
"intVal=" + intVal +
", stringVal='" + stringVal + '\'' +
'}';
}
public static void main(String[] args) throws IOException, ClassNotFoundException {
EntityClass a = new EntityClass(1, "1");
EntityClass b = new EntityClass(2, "2");
EntityClass c = new EntityClass(3, "3");
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("out"));
stream.writeObject(a);
stream.writeObject(b);
stream.writeObject(c);
stream.close();
ObjectInputStream streamRead = new ObjectInputStream(new FileInputStream("out"));
EntityClass[] entities = new EntityClass[3];
int cont = 0;
try {
while (streamRead.available() >= 0) {
entities[cont] = (EntityClass) streamRead.readObject();
System.out.println(entities[cont]);
cont++;
}
} catch (EOFException exp) {
} finally {
streamRead.close();
}
}
}

Sending a boolean value to a PLC from Android

I was able to make a connection with a PLC to read data from it. Now there's one problem and it is that I have to write a method to modify the data from the PLC. To achieve this, I have to send two values to the PLC: an int value and a boolean value. I got the int value solved via the classes from the net.wimpi.modbus package. But when it comes to the boolean value I have no clue what to do.
If someone had the same problem as I do now, could you please send me a reference where I can find a solution or a link of a really good tutorial to solve my problem? Someone posted a couple of links in this question but it sends me to tutorials that doesn't have much to do with the communication with the PLC's and how to treat the data of the PLC.
EDIT
I made the connection with a Modicon M340 PLC, and for the connection I use the classes of the net.wimpi.modbus package. I made the connection in my code through the classes ModbusTCPTransaction and TCPMasterConnection, and I read the values through the classes ReadMultipleRegistersRequest and ReadMultipleRegistersResponse.
The code I made for the connection:
private InetAddress m_Address;
private ModbusTCPTransaction m_Transaction = null;
private TCPMasterConnection m_Connection = null;
int port = Modbus.DEFAULT_PORT;
private Activity activity;
public ModbusConnection(Activity activity, String ip)
{
this.activity = activity;
try {
m_Address = InetAddress.getByName(ip);
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} // the slave'saddress
}
public void getTransaction(InetAddress inet) throws Exception
{
/*Variables for the reading of the PLC data*/
int port = Modbus.DEFAULT_PORT;
/*Data initialization for the reading of the PLC data*/
m_Connection = new TCPMasterConnection(inet);
m_Connection.setPort(port);
m_Connection.connect();
m_Transaction = new ModbusTCPTransaction(m_Connection);
}
And to read the values, I call the next code all the time. I accomplished only reading and writing int, String and float values through words that I read from an offset declared on the PLC:
private ReadMultipleRegistersResponse readValues(int offset, int count)
{
ReadMultipleRegistersRequest rReq = null; // the request
ReadMultipleRegistersResponse rRes = null; // the response
try {
rReq = new ReadMultipleRegistersRequest(offset, count);
m_Transaction.setRequest(rReq);
m_Transaction.execute();
rRes = (ReadMultipleRegistersResponse) m_Transaction.getResponse();
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
}
return rRes;
}
EDIT 2
I think I accomplished what I wanted. There are 4 classes that I use to read the coils:
ReadCoilsRequest
ReadCoilsResponse
WriteMultipleCoilsRequest
WriteMultileCoilsResponse
What I did is two methods to read and write coils into the PLC:
private ReadCoilsResponse readBytes(int offset, int count)
{
ReadCoilsRequest rReq = null; // the request
ReadCoilsResponse rRes = null; // the response
try {
rReq = new ReadCoilsRequest(offset, count);
m_Transaction.setRequest(rReq);
m_Transaction.execute();
rRes = (ReadCoilsResponse) m_Transaction.getResponse();
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
}
return rRes;
}
public void writeBytes(int wordNumber, BitVector b) {
try {
WriteMultipleCoilsRequest wReq = null; //
WriteMultipleCoilsResponse wRes = null; //
wReq = new WriteMultipleCoilsRequest(211, b);
m_Transaction.setRequest(wReq);
m_Transaction.execute();
} catch (Exception e) {
e.printStackTrace();
Log.i("AsyncTask", "doInBackground: Exception");
}
}
Also, I made a method to read BitVector variables using the Coils classes:
public BitVector readBitVector(int offset, int count)
{
BitVector myBitVector;
ReadCoilsResponse rRes = readBytes(offset, count);
rRes = (ReadCoilsResponse) m_Transaction.getResponse();
myBitVector = rRes.getCoils();
return myBitVector;
}
After this, what I used to set the bit to 1 or 0 is using a native function from the BitVector class from the net.wimpi.modbus.util package in my code:
test.setBit(2, true);
NOTE: It is important to remember that everytime that you want to read or write values to the plc, the best way to do it is opening a closing the connection to the PLC.
My definitive answer is: you have to treat the registers as bits. So if you want to write the second bit of a register represented in an int value in your code, you will have to do something like this:
intValue = m_Data[1].getValue();
intValue = intValue | 2;
m_Data[1].setValue(intValue);
The second line modifies the bit I want to write in my PLC.

Having to recreate ObjectOutputStream every time

I have seen a few people ask similar questions but the only answer anyone ever posts is that you shouldn't have to do it.
But I've tested it both ways - and it only works this way.
Server Side
try {
// Obtain input and output streams to the client
while(true) {
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
Object input = in.readObject();
if(input == RequestEnums.GETCURRENTGRID) {
out.writeObject(ContagionServerData.getImagePixels());
out.writeObject(ContagionServerData.getImageHeight());
out.writeObject(ContagionServerData.getImageWidth());
}
}
} catch( Exception e ) {
e.printStackTrace();
}
Client Side
try {
inputStream = new ObjectInputStream(serverSocket.getInputStream());
outputStream = new ObjectOutputStream(serverSocket.getOutputStream());
outputStream.writeObject(RequestEnums.GETCURRENTGRID);
int[] imagePixels = (int[]) inputStream.readObject();
int imageHeight = (Integer) inputStream.readObject();
int imageWidth = (Integer) inputStream.readObject();
copyImage(background, imagePixels, imageHeight, imageWidth);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
This works all day long.
But if I change it to this -
try {
// Obtain input and output streams to the client
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream());
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
while(true) {
Object input = in.readObject();
if(input == RequestEnums.GETCURRENTGRID) {
out.writeObject(ContagionServerData.getImagePixels());
out.writeObject(ContagionServerData.getImageHeight());
out.writeObject(ContagionServerData.getImageWidth());
out.flush();
}
}
} catch( Exception e ) {
e.printStackTrace();
}
(I have created the input and output stream farther up in the code)
try {
outputStream.writeObject(RequestEnums.GETCURRENTGRID);
outputStream.flush();
int[] imagePixels = (int[]) inputStream.readObject();
int imageHeight = (Integer) inputStream.readObject();
int imageWidth = (Integer) inputStream.readObject();
copyImage(background, imagePixels, imageHeight, imageWidth);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Then I successfully receive the correct data the first time from the server - but everytime after that - I just receive the same data and not the updated data and no errors as to why.
When you send data on an object stream, it will send each object only once. This means if you modify and object and send it multiple times, you need to either use writeUnshared(mutableObject) or reset() which clears the cache of objects already sent.
You can't create an ObjectOutput/InputStream repeatly. If you want to make sure data is sent instead of buffered use flush(). If you are sending data like int instead of Objects, try DataOutput/InputStream.
See the Javadoc for ObjectOutputStream.reset() and ObjectOutputStream.writeUnshared().

Categories

Resources