Doe Joe
Doe Joe

Reputation: 137

Java: Sending/Receiving int array via socket to/from a program coded in C

I would like to send an entire integer array from Java to another program which is coded in C, and vice versa for receiving.

I have read from here that i should use short array in Java while using normal int array in the C program.

Being new to Java, I'm still not very sure how to do this correctly. Below is my code:

package tcpcomm;

import java.io.IOException;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.util.Scanner;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

import datatypes.Message;

public class PCClient {

    public static final String C_ADDRESS = "192.168.1.1";
    public static final int C_PORT = 8888;

    private static PCClient _instance;
    private Socket _clientSocket;
        private ByteArrayOutputStream _toCProgram;
        private ByteArrayInputStream _fromCProgram;

    private PCClient() {

    }

    public static PCClient getInstance() {
        if (_instance == null) {
            _instance = new PCClient();
        }
        return _instance;
    }

    public static void main (String[] args) throws UnknownHostException, IOException {
        int msg[] = {Message.READ_SENSOR_VALUES};
        ByteArrayOutputStream out = new ByteArrayOutputStream();

    PCClient pcClient = PCClient.getInstance();
    pcClient.setUpConnection(C_ADDRESS, C_PORT);
    System.out.println("C program successfully connected");
    while (true) {
        pcClient.sendMessage(out, msg);
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        int[] msgReceived = pcClient.readMessage(in);
      } 
    }

    public void setUpConnection (String IPAddress, int portNumber) throws UnknownHostException, IOException{
        _clientSocket = new Socket(C_ADDRESS, C_PORT);
        _toCProgram = new PrintWriter(_clientSocket.getOutputStream());
        _fromCProgram = new Scanner(_clientSocket.getInputStream());
    }

    public void closeConnection() throws IOException {
        if (!_clientSocket.isClosed()) {
            _clientSocket.close();
        }
    }

    public void sendMessage(OutputStream out, int[] msg) throws IOException {
        int count = 0;
        DataOutputStream dataOut = new DataOutputStream(out);
        dataOut.writeInt(msg.length);
        System.out.println("Message sent: ");
        for (int e : msg) {
            dataOut.writeInt(e);
            System.out.print(e + " ");
            if(count % 2 == 1)
                System.out.print("\n");
            count++;
        }
        dataOut.flush();
    }

    public int[] readMessage(InputStream in) throws IOException {
        int count = 0;
        DataInputStream dataIn = new DataInputStream(in);
        int[] msg = new int[dataIn.readInt()];
        System.out.println("Message received: ");
        for (int i = 0; i < msg.length; ++i) {
            msg[i] = dataIn.readInt();
            System.out.print(msg[i] + " ");
            if(count % 2 == 1)
                System.out.print("\n");
            count++;
        }
        return msg;
    }
}

Any guidance on how to correct the code appreciated!!

Do i need to change the stream from bytearray to intarray? Sounds like a lot of conversion.. (Based on answers I can find here)

Upvotes: 3

Views: 5870

Answers (1)

Brian
Brian

Reputation: 892

To answer your question: No, you don't need to change streams from byte to int. Part of the nature of I/O streams in Java is that they are byte-based. So when you want to write an int to a stream you need to split it into its byte elements and write them each.

Java uses 4 byte signed integers and you cannot change that. For your counterpart (your C program) you need to make sure that it uses an equivalent type, such as int32_t from stdint.h. This can be considered as part of your "protocol".

Java already provides the means to read and write int, long, etc. values from and to streams with java.io.DataInputStream and java.io.DataOutputStream. It is important to keep in mind that DataOutputStream#writeInt(int) writes the four bytes of an int high to low. This is called Endianness, but I'm pretty sure you know that already. Also this can be considered as another part of your "protocol".

Lastly, when transmitting a structure, the endpoint that's reading must know how much it has to read. For fixed size structures both sides (client and server) know the size already, but arrays can vary. So when sending, you need to tell your counterpart how much you will be sending. In case of arrays it's pretty simple, just send the array length (just another int) first. This can be considered as yet another part of your "protocol".

I've written an example for writing and reading int arrays to and from streams.

package com.acme;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;

public class SendAndReceive {

    public static void main(String[] args) throws IOException {
        int[] ints = new int[] {Integer.MIN_VALUE, -1, 0, 1, Integer.MAX_VALUE};

        ByteArrayOutputStream out = new ByteArrayOutputStream();
        writeInts(out, ints);
        ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
        int[] results = readInts(in);

        if (!Arrays.equals(ints, results)) System.out.println("Damn!");
        else System.out.println("Aaall's well!");
    }

    private static void writeInts(OutputStream out, int[] ints) throws IOException {
        DataOutputStream dataOut = new DataOutputStream(out);
        dataOut.writeInt(ints.length);
        for (int e : ints) dataOut.writeInt(e);
        dataOut.flush();
    }

    private static int[] readInts(InputStream in) throws IOException {
        DataInputStream dataIn = new DataInputStream(in);
        int[] ints = new int[dataIn.readInt()];
        for (int i = 0; i < ints.length; ++i) ints[i] = dataIn.readInt();
        return ints;
    }
}

All you need to do is use the methods writeInts(OutputStream, int[]) and readInts(InputStream) and call them with your socket streams.

I've not implemented a C program for demonstration purposes, because there's no standard socket implementation (although there's the Boost library, but it's not standard and not C but C++).

Have fun!

Upvotes: 2

Related Questions