Java nio 仅读取 8192/433000 字节

Java nio Only Reading 8192/433000 bytes

我有一个项目正在致力于更好地理解 Java NIO 和网络编程的东西。我正在尝试通过 netcat 将一个超过 400,000 字节的文件发送到我的服务器,在那里可以找到该文件并将其写入文件。

问题: 当文件小于 10,000 字节时,或者当我在 Select() 之前放置 Thread.sleep(超时)时,该程序可以完美运行。该文件发送过来但只读取了 8192 个字节,然后取消循环并返回到 select() 以捕获其余数据。然而,该文件捕获了之后发生的事情。我需要完整的数据以进一步扩展项目。

我尝试过的事情: 我试图将数据加载到另一个显然有效的字节数组中,但跳过了 8192 个字节(因为 select() 已被再次调用)。读取剩余的 391000 字节。比较文件时,前 8192 个字节丢失。 我尝试了其他各种方法,但我对 NIO 的了解还不够,无法理解我在搞砸什么。

我的代码

这就是我觉得代码乱七八糟的地方(调试后)

private void startServer() {
   File temp = new File("Filepath");
   Selector selector = Selector.open();
   ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
   serverSocketChannel.configureBlocking(false);
   serverSocketChannel.socket().bind(listenAddress);
   serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

   log.info("Server Socket Channel Started");

   while(!stopRequested){
     selector.select();
     Set<SelectionKey> keys = selector.selectedKeys();

     for(SelectionKey key : keys){
       if(key.isAcceptable()){
         try {
           serverSocketChannel = (ServerSocketChannel) key.channel();
           SocketChannel socket = serverSocketChannel.accept();
           socket.configureBlocking(false);
           socket.register(selector, SelectionKey.OP_READ);
         }catch (IOException e) {
           log.error("IOException caught: ", e);
         }
       }
       if(key.isReadable(){ read(key); }
       keys.remove(key);
     }
   } 
  } catch (IOException e) {
    log.error("Error: ", e);
  }
}

private void read(SelectionKey key) {
  int count = 0;
  File tmp = new File("Path");

  try {
    SocketChannel channel = (SocketChannel) key.channel();
    byteBuffer.clear();
    while((count = channel.read(byteBuffer)) > 0) {
         byteBuffer.flip();
         //in bytearrayoutputstream to append to data byte array
         byteArrayOutputStream.write(byteBuffer.array(), byteBuffer.arrayOffset(), count);
         byteBuffer.compact();
      }
    }
    data = byteArrayOutputStream.toByteArray();
    FileUtils.writeByteArrayToFile(tmp, data);
  }
}


上面的代码是我正在使用的。我在这个 class 中有更多内容,但我相信有问题的主要两个功能是这两个。我不太确定应该采取什么步骤。我必须测试我的程序的文件包含许多大约 400,000 字节的 TCP。 select() 收集最初的 8192 个字节,然后运行读取(在它捕获流中的所有数据之前不应该发生),返回并收集其余部分。我已将 byteBuffer 分配为 30720 字节。

如果不清楚,我可以post剩下的代码,让我知道你的建议是什么。

问题
为什么当分配的 space 是 30720 时这段代码只抓取 8192 字节?为什么它在调试模式或 Thread.sleep() 下工作?

之前有人建议我将 byteBuffer.clear() 放在循环之外,即使这样做了,问题仍然存在。

非阻塞 API 仅仅承诺如果超过 0 个字节就会引发 'readable' 状态。它不保证它会等到您感兴趣的所有字节都到达;甚至没有办法说 'dont mark this channel as isReadable until at least X bytes are in'。没有办法直接解决这个问题;您的代码必须能够处理半满缓冲区。例如,通过读取此数据以便清除 'isReadable' 状态,直到更多字节到达。

使用原始非阻塞 APIs 是火箭科学(因为,正确编写代码非常棘手,很容易让 CPU 核心旋转到 100 % 因为您对标志的管理不当,并且很容易冻结所有线程,并且由于意外调用阻塞方法,应用程序只能处理正常线程变体可能完成的工作的百分之一或两个。

我强烈建议你首先重新考虑你是否需要非阻塞(它总是几乎慢,而且开发起来更难几个数量级。毕竟,你不能在任何处理程序代码中的任何地方进行单个潜在阻塞调用或者你的应用程序在负载下会变慢,而且 java 不擅长 await/yield 东西——唯一真正的好处是你可以更精细地控制缓冲区大小,除非你的内存很大,否则这是无关紧要的受限并且可以为每个连接状态使用微小的缓冲区)。如果您随后得出结论认为这确实是唯一的方法,请使用使 API 更易于使用的库,例如 netty.