Java nio仅读取8192/433000字节

Java nio仅读取8192/433000字节,java,networking,selector,nio,Java,Networking,Selector,Nio,为了更好地理解JavaNIO和网络编程,我正在进行一个项目。我正试图通过netcat将一个400000多字节的文件发送到我的服务器,在那里可以找到并写入一个文件 问题: 当文件小于10000字节时,或者当我在Select()之前放置一个Thread.sleep(timeout)时,该程序工作正常。该文件发送但只读取8192字节,然后取消循环并返回select()以捕获其余数据。但是,该文件捕获了后面的内容。我需要完整的数据进一步扩大到该项目 我尝试过的事情: 我试图将数据加载到另一个字节数组中,

为了更好地理解JavaNIO和网络编程,我正在进行一个项目。我正试图通过netcat将一个400000多字节的文件发送到我的服务器,在那里可以找到并写入一个文件

问题: 当文件小于10000字节时,或者当我在Select()之前放置一个Thread.sleep(timeout)时,该程序工作正常。该文件发送但只读取8192字节,然后取消循环并返回select()以捕获其余数据。但是,该文件捕获了后面的内容。我需要完整的数据进一步扩大到该项目

我尝试过的事情: 我试图将数据加载到另一个字节数组中,这显然是可行的,但跳过了8192个字节(因为select()再次被调用)。读取391000字节的其余部分。比较文件时,缺少前8192个字节。 我尝试过很多其他的事情,但我在NIO中还不足以理解我把事情搞砸了

我的代码

这正是我感觉代码混乱的地方(调试后)

private void startServer(){
File temp=新文件(“文件路径”);
选择器=选择器。打开();
ServerSocketChannel ServerSocketChannel=ServerSocketChannel.open();
serverSocketChannel.configureBlocking(false);
serverSocketChannel.socket().bind(listenAddress);
serverSocketChannel.register(选择器,SelectionKey.OP_ACCEPT);
log.info(“服务器套接字通道已启动”);
而(!stopRequested){
selector.select();
设置键=选择器。selectedKeys();
用于(选择按键:按键){
if(key.isAcceptable()){
试一试{
serverSocketChannel=(serverSocketChannel)key.channel();
SocketChannel socket=serverSocketChannel.accept();
socket.configureBlocking(false);
插座.寄存器(选择器,SelectionKey.OP_READ);
}捕获(IOE异常){
log.error(“捕获的IOException:”,e);
}
}
if(key.isReadable(){read(key);}
键。移除(键);
}
} 
}捕获(IOE异常){
log.error(“错误:”,e);
}
}
私有无效读取(SelectionKey){
整数计数=0;
文件tmp=新文件(“路径”);
试一试{
SocketChannel=(SocketChannel)key.channel();
byteBuffer.clear();
而((count=channel.read(byteBuffer))>0){
byteBuffer.flip();
//在bytearrayoutputstream中附加到数据字节数组
byteArrayOutputStream.write(byteBuffer.array()、byteBuffer.arrayOffset()、count);
byteBuffer.compact();
}
}
data=byteArrayOutputStream.toByteArray();
writeByteArrayToFile(tmp,数据);
}
}
上面的代码就是我正在使用的。我在这个类中有更多的内容,但我相信有问题的主要两个函数是这两个。我不太确定我应该采取什么步骤。我必须测试我的程序的文件包含许多大约400000字节的TCP。select()收集最初的8192字节,然后运行read(在它捕获流中的所有数据之前不应该发生),返回并收集其余的数据。我已将byteBuffer分配为30720字节

如果不清楚,我可以张贴代码的其余部分,让我知道你的建议是什么

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


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

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

使用原始的非阻塞API是火箭科学(如中所述,正确编写代码非常棘手,很容易让CPU内核旋转到100%,因为您对标志管理不当,很容易冻结所有线程,并且由于意外调用阻塞方法,应用程序只能处理普通线程变体所能处理的1%或2%


我强烈建议您首先重新考虑是否需要非阻塞(它总是很慢,而且开发起来难度也很大。毕竟,你不能在任何处理程序代码的任何地方进行一次潜在的阻塞调用,否则你的应用程序在加载时都会非常慢,而且java在等待/产生东西方面也不是很好——唯一真正的好处是你可以更细粒度地控制缓冲区大小,这是不可改变的vant(除非您的RAM非常有限,并且可以为每个连接状态使用微型缓冲区)。如果您认为这确实是唯一的方法,那么请使用一个使此API更易于使用的库,例如。

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

使用原始的非阻塞API是火箭科学(如中所述,正确编写代码非常困难,很容易使CPU内核旋转到100%,因为您管理不正确的标志,并且很容易
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);
  }
}