简体   繁体   English

Java nio 仅读取 8192/433000 字节

[英]Java nio Only Reading 8192/433000 bytes

I have a project that I'm working on to better understand Java NIO and network programming stuff.我有一个项目,我正在致力于更好地理解 Java NIO 和网络编程的东西。 I'm trying to send a 400,000+ byte file through netcat to my server where it will be found and written to a file.我正在尝试通过 netcat 将一个 400,000+ 字节的文件发送到我的服务器,在那里可以找到它并将其写入文件。

The Problem: The program works perfectly when the file is below 10,000 bytes, or when I place a Thread.sleep(timeout) before the Select().问题:当文件小于 10,000 字节时,或者当我在 Select() 之前放置一个 Thread.sleep(timeout) 时,该程序运行良好。 The file sends over but only reads 8192 bytes and then cancels out of the loop and goes back to the select() to capture the rest of the data.文件发送过来但只读取 8192 字节,然后取消循环并返回到 select() 以捕获其余数据。 However the file captures what comes after.但是,该文件捕获了后面的内容。 I need the complete data for further expansion to the project.我需要完整的数据以进一步扩展项目。

Things I've Tried: I've tried to load the data onto another byte array which evidently works, but skips over the 8192 bytes (since the select() has been called again).我尝试过的事情:我尝试将数据加载到另一个显然有效的字节数组中,但跳过了 8192 个字节(因为再次调用了 select() )。 Reads the rest of the 391000 bytes.读取剩余的 391000 字节。 When comparing the files the first 8192 bytes is missing.比较文件时,前 8192 个字节丢失。 I've tried various other things but I'm not adequate in NIO to understand what I'm messing up on.我已经尝试了其他各种事情,但我在 NIO 中还不足以理解我在搞什么。

My Code我的代码

This is just where I feel the code is messing bout (after debugging)这就是我觉得代码混乱的地方(调试后)

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


The above code is what I'm working with.上面的代码是我正在使用的。 I have more stuff in this class but I believe the main two functions having the problem are these two.我在这门课上有更多的东西,但我相信有问题的主要两个功能是这两个。 I'm not too sure what steps I should take.我不太确定我应该采取什么步骤。 The file I have to test my program contains many TCPs about 400,000 bytes.我必须测试我的程序的文件包含许多大约 400,000 字节的 TCP。 The select() collects the initial 8192 bytes and then runs read (which shouldn't happen until it captures all of the data in the stream), comes back and gathers the rest. select() 收集初始的 8192 字节,然后运行读取(在它捕获流中的所有数据之前不应发生这种情况),然后返回并收集其余部分。 I've allocated the byteBuffer to be 30720 bytes.我已将 byteBuffer 分配为 30720 字节。

If not clear, I can post the rest of the code, let me know what your suggestions are.如果不清楚,我可以发布其余的代码,让我知道您的建议是什么。

Question
Why does this code only grab 8192 bytes when the allocated space is 30720?当分配的空间为30720时,为什么这段代码只抓取8192个字节? Why does it work in debug mode or with Thread.sleep()?为什么它在调试模式或与 Thread.sleep() 一起工作?

Previous person advised me to place my byteBuffer.clear() outside of loop, even after doing so, the problem persists.以前的人建议我将我的 byteBuffer.clear() 放在循环之外,即使这样做之后,问题仍然存在。

The non-blocking API merely promises that the 'readable' state is raised if there are more than 0 bytes.非阻塞 API 仅承诺如果有超过 0 个字节,则会引发“可读”状态。 It makes no guarantee that it'll wait until all the bytes you're interested in have arrived;它不能保证它会等到您感兴趣的所有字节都到达; there isn't even a way to say 'dont mark this channel as isReadable until at least X bytes are in'.甚至没有办法说“在至少有 X 个字节之前不要将此通道标记为isReadable ”。 There is no way to fix that directly;没有办法直接解决这个问题。 your code must instead be capable of dealing with a half filled buffer.您的代码必须能够处理半满的缓冲区。 For example, by either reading this data away so that the 'isReadable' state gets cleared until MORE bytes arrive.例如,通过读取此数据以便清除“isReadable”状态,直到更多字节到达。

Using the raw non-blocking APIs is rocket science (as in, it is very tricky to write your code correctly, it is easy to get a CPU core to spin to 100% because you're mismanaging your flags, and it is easy to have all threads frozen and the app reduced to being able to handle only a percent or two of what a normal threaded variant could have done due to accidental invocation of blocking methods.使用原始的非阻塞 API 是一项火箭科学(例如,正确编写代码非常棘手,很容易让 CPU 内核旋转到 100%,因为您对标志的管理不善,并且很容易冻结所有线程,并且应用程序减少到只能处理正常线程变体由于意外调用阻塞方法而可能完成的任务的百分之一或两个。

I strongly suggest you first reconsider whether you need non-blocking at all (it always almost slower, and orders of magnitude harder to develop for. After all, you cannot make a single potentially blocking call anywhere in any handler code or your app will be dog slow under load, and java is not great at await/yield stuff – the only real benefit is that you get more finegrained control over buffer sizes, which is irrelevant unless you are very RAM constrained and can get away with tiny buffers for per-connection state).我强烈建议你首先重新考虑你是否需要非阻塞(它总是几乎更慢,而且开发难度要大几个数量级。毕竟,你不能在任何处理程序代码的任何地方进行一个潜在的阻塞调用,否则你的应用程序将狗在负载下很慢,而且 java 不擅长等待/产量的东西——唯一真正的好处是你可以更细粒度地控制缓冲区大小,这无关紧要,除非你非常受 RAM 限制并且可以摆脱每个人的微小缓冲区连接状态)。 And if you then conclude that truly this is the only way, use a library that makes this API easier to use, such as netty .如果您随后得出结论,这确实是唯一的方法,请使用使此 API 更易于使用的库,例如netty

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM