【问题标题】:Java nio Only Reading 8192/433000 bytesJava nio 仅读取 8192/433000 字节
【发布时间】:2019-12-11 16:05:36
【问题描述】:

我正在开展一个项目,以更好地了解 Java NIO 和网络编程内容。我正在尝试通过 netcat 将 400,000+ 字节的文件发送到我的服务器,在那里可以找到它并将其写入文件。

问题: 当文件小于 10,000 字节时,或者当我在 Select() 之前放置一个 Thread.sleep(timeout) 时,该程序可以完美运行。文件发送过来但只读取 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);
  }
}


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

如果不清楚,我可以发布其余代码,让我知道您的建议。

问题
为什么分配空间为 30720 时,这段代码只抓取 8192 字节?为什么它在调试模式下或与 Thread.sleep() 一起工作?

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

【问题讨论】:

    标签: java networking selector nio


    【解决方案1】:

    非阻塞 API 仅承诺如果字节数超过 0,则会引发“可读”状态。它不能保证它会等到您感兴趣的所有字节都到达;甚至没有办法说“在至少有 X 个字节之前不要将此频道标记为isReadable”。没有办法直接解决这个问题;您的代码必须能够处理半满的缓冲区。例如,通过读取这些数据以便清除“isReadable”状态,直到有更多字节到达。

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

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

    【讨论】:

    • 感谢您的建议!如果我要尝试第一个建议并将数据丢弃,直到更多字节到达。我该怎么做呢?我是否存储接收到的数据,然后再次迭代,捕获其余数据?
    • 是的。从缓冲区中提取确实存在的字节,然后将其重置。或者:翻转它,阅读它以发现它是不完整的,确保你已经阅读它全部或明确地将位置设置回末尾并将限制设置为满容量,然后返回等待 isReadable。或者只是使用netty,真的。你不想重新发明这个轮子,相信我。
    猜你喜欢
    • 1970-01-01
    • 2018-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-05-15
    • 2013-10-26
    • 1970-01-01
    相关资源
    最近更新 更多