How to write array to OutputStream in Java?
Asked Answered
M

7

7

I want to send more than one random value though Socket. I think array is the best way to send them. But I don't know how to write an array to Socket OutputStream?

My Java class:

import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.Socket; import java.io.*; import java.util.Random;

public class NodeCommunicator {

    public static void main(String[] args) {
        try {
            Socket nodejs = new Socket("localhost", 8181);

            Random randomGenerator = new Random();
            for (int idx = 1; idx <= 1000; ++idx){
                Thread.sleep(500);
                int randomInt = randomGenerator.nextInt(35);
                sendMessage(nodejs, randomInt + " ");
                System.out.println(randomInt);
            }

            while(true){
                Thread.sleep(1000);
            }
        } catch (Exception e) {
            System.out.println("Connection terminated..Closing Java Client");
            System.out.println("Error :- "+e);
        }
    }

    public static void sendMessage(Socket s, String message) throws IOException {
        s.getOutputStream().write(message.getBytes("UTF-8"));
        s.getOutputStream().flush();
    }

}
Manned answered 21/12, 2012 at 4:9 Comment(3)
what is wrong with this code? what and how does not work?Toothed
what is the error you are gettingMeilhac
From his explanation, it looks like he want to send an array of messages in one call to write(), instead of iterating over the array and sending each string by stringEssequibo
W
8

Use java.io.DataOutputStream / DataInputStream pair, they know how to read ints. Send info as a packet of length + random numbers.

sender

Socket sock = new Socket("localhost", 8181);
DataOutputStream out = new DataOutputStream(sock.getOutputStream());
out.writeInt(len);
for(int i = 0; i < len; i++) {
      out.writeInt(randomGenerator.nextInt(35))
...

receiver

 DataInputStream in = new DataInputStream(sock.getInputStream());
 int len = in.readInt();
 for(int i = 0; i < len; i++) {
      int next = in.readInt();
 ...
Wengert answered 21/12, 2012 at 5:19 Comment(0)
C
4

Java arrays are actually Objects and moreover they implement the Serializable interface. So, you can serialize your array, get the bytes and send those through the socket. This should do it:

public static void sendMessage(Socket s, int[] myMessageArray)
   throws IOException {
  ByteArrayOutputStream bs = new ByteArrayOutputStream();
  ObjectOutputStream os = new ObjectOutputStream(bs);
  os.writeObject(myMessageArray);
  byte[] messageArrayBytes = bs.toByteArray();
  s.getOutputStream().write(messageArrayBytes);
  s.getOutputStream().flush();
}

What's really neat about this is that it works not only for int[] but for any Serializable object.

Edit: Thinking about it again, this is even simpler:

sender:

public static void sendMessage(Socket s, int[] myMessageArray)
   throws IOException {
  OutputStream os = s.getOutputStream();  
  ObjectOutputStream oos = new ObjectOutputStream(os);  
  oos.writeObject(myMessageArray); 
}

receiver:

public static int[] getMessage(Socket s)
   throws IOException {
  InputStream is = s.getInputStream();  
  ObjectInputStream ois = new ObjectInputStream(is);  
  int[] myMessageArray = (int[])ois.readObject(); 
  return myMessageArray;
}

I'm leaving my first answer also as(it also works and) it's useful for writing objects to UDP DatagramSockets and DatagramPackets where there is no stream.

Correy answered 21/12, 2012 at 5:41 Comment(5)
you need to send the length first otherwise you will not be able to read any thing after this.Aghast
@PeterLawrey explain? write(byte[] b) writes b.length bytes to the stream.Correy
Say you call this twice, how would you know when the first array finished and the next array starts? i.e. try reading this format and you will find it very difficult. btw +1Aghast
Ah I see..good point(I haven't actually tried doing this) and thanx:-)Correy
Thinking about it again: If I'm not mistaken, when the object is read on the receiver side, the bytes will be passed to an ObjectInputStream and that should be able to read the Object unambiguously without the length being sent. No?Correy
I
2

I would advice to simply concatenate the int values in a string using some delimiter e.g. @@, then transmit the final concatenated string at once. On the receiving side, just split the string using same delimiter to get the int[] back e.g.:

    Random randomGenerator = new Random();
    StringBuilder numToSend = new StringBuilder("");
    numToSend.append(randomGenerator.nextInt(35));

    for (int idx = 2; idx <= 1000; ++idx){
        Thread.sleep(500);
        numToSend.append("@@").append(randomGenerator.nextInt(35));
    }
    String numsString = numToSend.toString();
    System.out.println(numsString);
    sendMessage(nodejs, numsString);

On the receiving side, get your string and split as:

   Socket remotejs = new Socket("remotehost", 8181);
   BufferedReader in = new BufferedReader(
                               new InputStreamReader(remotejs.getInputStream()));
   String receivedNumString = in.readLine();
   String[] numstrings = receivedNumString.split("@@");
   int[] nums = new int[numstrings.length];
   int indx = 0;
   for(String numStr: numstrings){
     nums[indx++] = Integer.parseInt(numStr);
   }
Immoral answered 21/12, 2012 at 4:24 Comment(2)
+1 Using @@ is perhaps overkill for numbers. A plain space would do.Aghast
@PeterLawrey Agree. I already mentioned to use any delimiter.Immoral
S
1

Well, a stream is a stream of bytes, so you must encode the data into a sequence of bytes, and decode it on the receiving side. How to write the data depends on how you want to encode it.

As numbers from 0 to 34 fit in a single byte, this can be as easy as:

outputStream.write(randomNumber); 

and on the other side:

int randomNumber = inputStream.read();

Of course, flushing the stream after every byte is not very efficient (as it'll generate a network packet for every byte, and each network packet contains dozens of bytes of header and routing information ...). Should performance matter, you'll probably want to use a BufferedOutputStream, too.

Staub answered 21/12, 2012 at 4:48 Comment(0)
A
1

So you can compare the alternative formats I have written a template which allows you to use any format your wish, or compare the alternatives.

abstract class DataSocket implements Closeable {
    private final Socket socket;
    protected final DataOutputStream out;
    protected final DataInputStream in;

    DataSocket(Socket socket) throws IOException {
        this.socket = socket;
        out = new DataOutputStream(new BufferedOutputStream(socket.getOutputStream()));
        in = new DataInputStream(new BufferedInputStream(socket.getInputStream()));
    }

    public void writeInts(int[] ints) throws IOException {
        writeInt(ints.length);
        for (int i : ints)
            writeInt(i);
        endOfBlock();
    }

    protected abstract void writeInt(int i) throws IOException;

    protected abstract void endOfBlock() throws IOException;

    public int[] readInts() throws IOException {
        nextBlock();
        int len = readInt();
        int[] ret = new int[len];
        for (int i = 0; i < len; i++)
            ret[i] = readInt();
        return ret;
    }

    protected abstract void nextBlock() throws IOException;

    protected abstract int readInt() throws IOException;

    public void close() throws IOException {
        out.close();
        in.close();
        socket.close();
    }
}

binary format, 4-byte ints

class BinaryDataSocket extends DataSocket {
    BinaryDataSocket(Socket socket) throws IOException {
        super(socket);
    }

    @Override
    protected void writeInt(int i) throws IOException {
        out.writeInt(i);
    }

    @Override
    protected void endOfBlock() throws IOException {
        out.flush();
    }

    @Override
    protected void nextBlock() {
        // nothing
    }

    @Override
    protected int readInt() throws IOException {
        return in.readInt();
    }
}

stop bit encoded binary with one byte per 7 bits.

class CompactBinaryDataSocket extends DataSocket {
    CompactBinaryDataSocket(Socket socket) throws IOException {
        super(socket);
    }

    @Override
    protected void writeInt(int i) throws IOException {
        // uses one byte per 7 bit set.
        long l = i & 0xFFFFFFFFL;
        while (l >= 0x80) {
            out.write((int) (l | 0x80));
            l >>>= 7;
        }
        out.write((int) l);
    }

    @Override
    protected void endOfBlock() throws IOException {
        out.flush();
    }

    @Override
    protected void nextBlock() {
        // nothing
    }

    @Override
    protected int readInt() throws IOException {
        long l = 0;
        int b, count = 0;
        while ((b = in.read()) >= 0x80) {
            l |= (b & 0x7f) << 7 * count++;
        }
        if (b < 0) throw new EOFException();
        l |= b << 7 * count;
        return (int) l;
    }
}

text encoded with a new line at the end.

class TextDataSocket extends DataSocket {
    TextDataSocket(Socket socket) throws IOException {
        super(socket);
    }

    private boolean outBlock = false;

    @Override
    protected void writeInt(int i) throws IOException {
        if (outBlock) out.write(' ');
        out.write(Integer.toString(i).getBytes());
        outBlock = true;
    }

    @Override
    protected void endOfBlock() throws IOException {
        out.write('\n');
        out.flush();
        outBlock = false;
    }

    private Scanner inLine = null;

    @Override
    protected void nextBlock() throws IOException {
        inLine = new Scanner(in.readLine());
    }

    @Override
    protected int readInt() throws IOException {
        return inLine.nextInt();
    }
}
Aghast answered 21/12, 2012 at 9:40 Comment(0)
C
0

if you want to send more than one random value to the socket, choose a simple format and make both parties agree to it (sender and reciever), for example you can simply choose a separator like ; and create a string with all values with that separator and then send

Criminate answered 21/12, 2012 at 4:27 Comment(0)
L
0

Android Socket SERVER Example modified to receive arrays of integers instead of strings:

...
class CommunicationThread implements Runnable {

    private ObjectInputStream input;

    public CommunicationThread(Socket clientSocket) {

        try {

            this.input = new ObjectInputStream(clientSocket.getInputStream());

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void run() {

        while (!Thread.currentThread().isInterrupted()) {

            try {

                int[] myMessageArray = (int[]) input.readObject();

                String read = null;
                read = String.format("%05X", myMessageArray[0] & 0x0FFFFF);
                int i = 1;
                do {
                    read = read + ", " + String.format("%05X", myMessageArray[i] & 0x0FFFFF);
                    i++;
                } while (i < myMessageArray.length);
                read = read + " (" + myMessageArray.length + " bytes)";

                updateConversationHandler.post(new updateUIThread(read));

            } catch (IOException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
}
...
Laurenlaurena answered 21/12, 2014 at 18:22 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.