【问题标题】:How to write array to OutputStream in Java?如何在 Java 中将数组写入 OutputStream?
【发布时间】:2012-12-21 04:09:22
【问题描述】:

我想通过Socket 发送多个随机值。我认为数组是发送它们的最佳方式。但是不知道怎么写数组到SocketOutputStream

我的 Java 类:

导入 java.io.ByteArrayOutputStream; 导入 java.io.IOException; 导入 java.io.InputStream; 导入 java.net.Socket; 导入java.io.*; 导入 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();
    }

}

【问题讨论】:

  • 这段代码有什么问题?什么以及如何不起作用?
  • 您遇到的错误是什么
  • 从他的解释来看,他似乎想在一次调用 write() 时发送一组消息,而不是遍历数组并逐个字符串发送每个字符串

标签: java sockets outputstream


【解决方案1】:

使用 java.io.DataOutputStream / DataInputStream 对,他们知道如何读取整数。以长度 + 随机数的数据包形式发送信息。

发件人

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

接收者

 DataInputStream in = new DataInputStream(sock.getInputStream());
 int len = in.readInt();
 for(int i = 0; i < len; i++) {
      int next = in.readInt();
 ...

【讨论】:

    【解决方案2】:

    Java 数组实际上是Objects,而且它们实现了Serializable 接口。因此,您可以序列化您的数组,获取字节并通过套接字发送这些字节。应该这样做:

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

    真正巧妙的是,它不仅适用于int[],还适用于任何Serializable 对象。

    编辑: 再想一想,这就更简单了:

    发件人

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

    接收者

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

    我也将我的第一个答案作为(它也有效并且)在没有流的情况下将对象写入 UDP DatagramSocketsDatagramPackets 很有用。

    【讨论】:

    • 您需要先发送长度,否则之后您将无法阅读任何内容。
    • @PeterLawrey 解释一下? write(byte[] b)b.length 字节写入流。
    • 假设你调用了两次,你怎么知道第一个数组何时结束,下一个数组何时开始?即尝试阅读这种格式,您会发现它非常困难。顺便说一句 +1
    • 啊我明白了..好点(我实际上没有尝试过这样做)和thanx:-)
    • 再想一想:如果我没记错的话,当在接收端读取对象时,字节将被传递给 ObjectInputStream 并且应该能够在没有发送的长度。没有?
    【解决方案3】:

    我建议使用一些分隔符将 int 值简单地连接到一个字符串中,例如@@,然后立即传输最终连接的字符串。在接收端,只需使用相同的分隔符拆分字符串即可返回 int[],例如:

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

    在接收端,获取您的字符串并拆分为:

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

    【讨论】:

    • +1 使用@@ 对数字来说可能是多余的。一个普通的空间就可以了。
    • @PeterLawrey 同意。我已经提到使用任何分隔符。
    【解决方案4】:

    好吧,流是字节流,因此您必须将数据编码为字节序列,并在接收端对其进行解码。如何写入数据取决于您要如何对其进行编码。

    因为从 0 到 34 的数字适合单个字节,所以这很简单:

    outputStream.write(randomNumber); 
    

    另一方面:

    int randomNumber = inputStream.read();
    

    当然,在每个字节之后刷新流并不是很有效(因为它会为每个字节生成一个网络数据包,并且每个网络数据包都包含数十个字节的标头和路由信息......)。如果性能很重要,您可能还想使用 BufferedOutputStream。

    【讨论】:

      【解决方案5】:

      因此您可以比较替代格式,我已经编写了一个模板,该模板允许您使用您希望使用的任何格式,或者比较替代方案。

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

      二进制格式,4 字节整数

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

      停止位编码二进制,每 7 位一个字节。

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

      在末尾用新行编码的文本。

      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】:

        如果您想向套接字发送多个随机值,请选择一种简单的格式并让双方同意(发送方和接收方),例如您可以简单地选择像; 这样的分隔符并创建一个字符串使用该分隔符的所有值,然后发送

        【讨论】:

          【解决方案7】:

          Android Socket SERVER Example 修改为接收整数数组而不是字符串:

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

          【讨论】:

            猜你喜欢
            • 2013-11-25
            • 1970-01-01
            • 2011-07-04
            • 1970-01-01
            • 2016-10-19
            • 1970-01-01
            • 1970-01-01
            • 2012-04-26
            • 1970-01-01
            相关资源
            最近更新 更多