繁体   English   中英

Java NIO:transferFrom直到流结束

[英]Java NIO: transferFrom until end of stream

我正在玩NIO库。 我正在尝试在端口8888上侦听连接,一旦接受连接,就将所有内容从该通道转储到somefile

我知道如何使用ByteBuffers ,但我想让它与所谓的超高效FileChannel.transferFrom

这就是我得到的:

ServerSocketChannel ssChannel = ServerSocketChannel.open();
ssChannel.socket().bind(new InetSocketAddress(8888));

SocketChannel sChannel = ssChannel.accept();
FileChannel out = new FileOutputStream("somefile").getChannel();

while (... sChannel has not reached the end of the stream ...)     <-- what to put here?
    out.transferFrom(sChannel, out.position(), BUF_SIZE);

out.close();

所以,我的问题是:我如何表达“从一些频道transferFrom直到达到流尾”


编辑:将1024更改为BUF_SIZE,因为所使用的缓冲区大小与问题无关。

处理案件的方法很少。 一些背景信息如何在内部实现trasnferTo / From以及何时可以实现。

  • 首先,您应该知道xfer需要多少字节,即使用FileChannel.size()来确定可用的最大值并对结果求和。 案例指的是FileChannel.trasnferTo(socketChanel)
  • 该方法不返回-1
  • 该方法在Windows上进行模拟。 Windows没有从filedescriptor到socket xfer的API函数,它确实从name指定的文件中有一(2)到xfer - 但这与java API不兼容。
  • 在Linux上,使用标准的sendfile (或sendfile64),在Solaris上称为sendfilev64

简而言之for (long xferBytes=0; startPos + xferBytes<fchannel.size();) doXfer()将用于从文件 - >套接字传输。 没有OS函数从socket转移到文件(OP感兴趣)。 由于套接字数据不是操作系统缓存,因此无法有效地完成,它是模拟的。 实现副本的最佳方法是使用带有套接字读缓冲区的轮询直接ByteBuffer标准循环。 因为我只使用涉及选择器的非阻塞IO。

话虽这么说: 我想让它与所谓的超高效“? - 它效率不高并且它在所有操作系统上模拟,因此当套接字正常关闭时它将最终转移。该功能将如果有任何转移(如果套接字是可读和打开的),甚至不抛出继承的IOException。

我希望答案很清楚:当源是文件时,会发生File.transferFrom唯一有趣的用法。 最有效(也是最有趣的情况)是file-> socket和file-> file是通过filechanel.map / unmap(!!)实现的

我不确定,但JavaDoc说:

尝试从源通道读取计数字节,并将它们从给定位置开始写入该通道的文件。 调用此方法可能会也可能不会传输所有请求的字节; 是否这样做取决于渠道的性质和状态 如果源通道剩余的字节数少于计数字节,或者源通道非阻塞且输入缓冲区中可立即使用的字节数少于计数字节,则将传输少于请求的字节数。

我想你可以说告诉它复制无限字节(当然不是在循环中)将完成这项工作:

out.transferFrom(sChannel, out.position(), Integer.MAX_VALUE);

所以,我想当套接字连接关闭时,状态将会改变,这将停止transferFrom方法。

但正如我已经说过的那样:我不确定。

直接回答你的问题:

while( (count = socketChannel.read(this.readBuffer) )  >= 0) {
   /// do something
}

但是,如果这是你做的,你不会使用非阻塞IO的任何好处,因为你实际上使用它完全阻止IO。 非阻塞IO的意思是1个网络线程可以同时为多个客户端服务:如果没有任何东西可以从一个通道读取(即count == 0 ),您可以切换到其他通道(属于其他客户端连接)。

因此,循环应该实际迭代不同的通道,而不是从一个通道读取,直到它结束。

看看这个教程: http//rox-xmlrpc.sourceforge.net/niotut/我相信它会帮助你理解这个问题。

据称是超高效的FileChannel.transferFrom。

如果你想要DMA访问和非阻塞IO的好处,最好的方法是对文件进行内存映射,然后只需从套接字读入内存映射缓冲区。

但这需要您预先分配文件。

这条路:

URLConnection connection = new URL("target").openConnection();
File file = new File(connection.getURL().getPath().substring(1));
FileChannel download = new FileOutputStream(file).getChannel();

while(download.transferFrom(Channels.newChannel(connection.getInputStream()),
        file.length(), 1024) > 0) {
    //Some calculs to get current speed ;)
}

transferFrom()返回一个计数。 只需继续调用它,推进位置/偏移,直到它返回零。 但是从大于1024的计数开始,更像是一兆字节或两兆,否则你不会从这种方法中获得太多好处。

编辑为了解决下面的所有评论,文档说“如果源通道剩余的字节数少于计数字节,或者如果源通道是非阻塞且立即数少于字节数,则将传输少于请求的字节数在其输入缓冲区中可用。“ 因此,如果您处于阻塞模式,它将不会返回零,直到源中没有任何内容。 因此循环直到它返回零是有效的。

编辑2

转移方法肯定是错误设计的。 应该将它们设计为在流结束时返回-1,就像所有read()方法一样。

建立在其他人写的基础上,这是一个简单的帮助方法,它实现了目标:

public static void transferFully(FileChannel fileChannel, ReadableByteChannel sourceChannel, long totalSize) {
    for (long bytesWritten = 0; bytesWritten < totalSize;) {
        bytesWritten += fileChannel.transferFrom(sourceChannel, bytesWritten, totalSize - bytesWritten);
    }
}

暂无
暂无

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

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