how to write array to outputStream in Java

2019-01-25 14:59发布

问题:

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;

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();
        }




 }

回答1:

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();
 ...


回答2:

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.



回答3:

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);
   }


回答4:

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.



回答5:

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();
    }
}


回答6:

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



回答7:

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();
            }
        }
    }
}
...