【问题标题】:Java data object for bidirectional I/O用于双向 I/O 的 Java 数据对象
【发布时间】:2008-12-12 13:34:27
【问题描述】:

我正在开发一个接口,该接口将加密的字节流作为输入——可能是一个非常的——生成或多或少相同格式的输出。

输入格式是这样的:

{N byte envelope}
    - encryption key IDs &c.
{X byte encrypted body}

输出格式相同。

这是通常的用例(当然是大量伪代码):

Message incomingMessage = new Message (inputStream);

ProcessingResults results = process (incomingMessage);

MessageEnvelope messageEnvelope = new MessageEnvelope ();
// set message encryption options &c. ...

Message outgoingMessage = new Message ();
outgoingMessage.setEnvelope (messageEnvelope);

writeProcessingResults (results, message);

message.writeToOutput (outputStream);

对我来说,使用同一个对象来封装这种行为似乎是有意义的,但我不知道应该如何去做。一次加载所有加密的正文是不切实际的;我需要能够对其进行流式传输(因此,我将使用某种输入流过滤器对其进行解密),但同时我需要能够写出该对象的新实例。什么是使这项工作的好方法? Message 内部应该是什么样子?

【问题讨论】:

    标签: java io


    【解决方案1】:

    我不会创建一个类来处理输入和输出 - 一个类,一项责任。我想要两个过滤器流,一个用于输入/解密,一个用于输出/加密:

    InputStream decrypted = new DecryptingStream(inputStream, decryptionParameters);
    ...
    OutputStream encrypted = new EncryptingStream(outputSream, encryptionOptions);
    

    他们可能有类似惰性初始化机制的东西,在第一次read() 调用之前读取信封/在第一次write() 调用之前写入信封。您还可以在过滤器实现中使用 Message 或 MessageEnvelope 等类,但它们可能会保持包保护的非 API 类。

    处理过程对仅在流上工作的解密/加密一无所知。在处理流式处理输入和输出的过程中,您也可以同时使用两个流进行输入和输出。

    【讨论】:

      【解决方案2】:

      你可以在任意位置分割身体吗?

      如果是这样,我将有两个线程,输入线程和输出线程,并有一个输出线程监控的并发字符串队列。比如:

      ConcurrentLinkedQueue<String> outputQueue = new ConcurrentLinkedQueue<String>();
      ...
      
      private void readInput(Stream stream) {
          String str;
          while ((str = stream.readLine()) != null) {
             outputQueue.put(processStream(str));
          }
      }
      
      private String processStream(String input) {
          // do something
          return output;
      }
      
      private void writeOutput(Stream out) {
          while (true) {
              while (outputQueue.peek() == null) {
                  sleep(100);
              }
      
              String msg = outputQueue.poll();
              out.write(msg);
          }
      }
      

      注意:这绝对不会按原样工作。只是一个设计建议。欢迎有人对此进行编辑。

      【讨论】:

      • 正文将是 XML,但我认为这无关紧要。传入的消息永远不会被写入,传出的消息永远不会被读取,但除此之外它们是相同的。
      【解决方案3】:

      如果您需要同时读取和写入,则必须使用线程(不同线程读取和写入)或异步 I/O(java.nio 包)。使用来自不同线程的输入和输出流不是问题。

      如果你想用java做一个流API,你通常应该提供InputStream用于读取和OutputStream用于写入。这样就可以将它们传递给其他 API,这样您就可以链接事物,从而让流一直作为流传输。

      输入示例:

      Message message = new Message(inputStream);
      results = process(message.getInputStream());
      

      输出示例:

      Message message = new Message(outputStream);
      writeContent(message.getOutputStream());
      

      消息需要使用执行所需加密和解密的类来包装给定的流。

      请注意,同时读取多条消息或同时写入多条消息也需要协议的支持。您需要正确同步。

      【讨论】:

      • 不需要多个线程来支持“同时”读写。在这种情况下,如果不提供任何实际好处,引入线程会使其变得更加困难。
      【解决方案4】:

      您应该检查Wikipedia article 支持流加密的不同分组密码模式。不同的加密算法可能支持其中的一个子集。

      缓冲流将允许您在循环中读取、加密/解密和写入。

      演示 ZipInputStream 和 ZipOutputStream 的示例可以为您如何解决此问题提供一些指导。见example

      【讨论】:

        【解决方案5】:

        您需要的是使用密码流 (CipherInputStream)。这是example 的使用方法。

        【讨论】:

          【解决方案6】:

          我同意Arne,数据处理器不应该知道加密,它只需要读取消息的解密正文并写出结果,流过滤器应该负责加密。但是,由于这在逻辑上是对同一条信息(一条消息)进行操作,我认为它们应该封装在一个处理消息格式的类中,尽管加密/解密流确实独立于这个。

          这是我对结构的想法,稍微翻转了架构,并将 Message 类移到加密流之外:

          class Message {
            InputStream input;
            Envelope envelope;
          
            public Message(InputStream input) {
              assert input != null;
              this.input = input;
            }
          
            public Message(Envelope envelope) {
              assert envelope != null;
              this.envelope = envelope;
            }
          
            public Envelope getEnvelope() {
              if (envelope == null && input != null) {
                // Read envelope from beginning of stream
                envelope = new Envelope(input);
              }
              return envelope
            }
          
            public InputStream read() {
              assert input != null
          
              // Initialise the decryption stream
              return new DecryptingStream(input, getEnvelope().getEncryptionParameters());
            }
          
            public OutputStream write(OutputStream output) {
              // Write envelope header to output stream
              getEnvelope().write(output);
          
              // Initialise the encryption
              return new EncryptingStream(output, getEnvelope().getEncryptionParameters());
            }
          }
          

          现在您可以通过为输入创建一条新消息并为输出创建一条新消息来使用它: 输出流输出; // 这是发送消息的流 消息输入消息 = 新消息(输入); 消息 outputMessage = new Message(inputMessage.getEnvelope()); 进程(inputMessage.read(),outputMessage.write(输出));

          现在处理方法只需要根据需要从输入中读取数据块,并将结果写入输出:

          public void process(InputStream input, OutputStream output) {
            byte[] buffer = new byte[1024];
            int read;
            while ((read = input.read(buffer) > 0) {
              // Process buffer, writing to output as you go.
            }
          }
          

          现在这一切都在同步进行,您不需要任何额外的线程。您也可以提前中止,而无需处理整个消息(例如,如果输出流已关闭)。

          【讨论】:

          • 你不应该像这样弄脏你的逻辑。删除一个构造函数并让用户承担创建信封的责任。现在,如果用户使用构造函数 1,则需要在调用 getOutputStream() 之前调用 getEnvelope()。我希望你明白为什么这是一个糟糕的设计。
          • 我已经稍微调整了我的答案,wds。我认为用户不必自己阅读信封 - 如果他们将流传递给错误的阅读器,则会导致混乱。我想应该都在Message里面。用户不需要知道将内容写入流的协议。
          猜你喜欢
          • 2016-08-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-05-02
          • 1970-01-01
          相关资源
          最近更新 更多