Java NIO:从传输开始直到流结束
我正在使用 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,因为使用的缓冲区大小与问题无关。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

发布评论
评论(7)
直接回答你的问题:
while( (count = socketChannel.read(this.readBuffer) ) >= 0) {
/// do something
}
但如果这就是你所做的,你就不会使用非阻塞 IO 的任何好处,因为你实际上将它用作阻塞 IO。非阻塞 IO 的要点是 1 个网络线程可以同时服务多个客户端:如果一个通道没有任何内容可读取(即 count == 0
),您可以切换到其他通道(属于到其他客户端连接)。
因此,循环实际上应该迭代不同的通道,而不是从一个通道读取直到结束。
看看本教程:http://rox-xmlrpc.sourceforge.net/niotut/
我相信它会帮助你理解这个问题。
我不确定,但 JavaDoc 说:
尝试从源通道读取最多 count 个字节
并将它们从给定位置开始写入该通道的文件。
调用此方法可能会也可能不会传输所有
请求的字节;是否这样做取决于性质
以及通道的状态。少于请求的字节数
如果源通道少于 count 字节,则将被传输
剩余,或者如果源通道是非阻塞的且少于
计算输入缓冲区中立即可用的字节数。
我想你可能会说告诉它复制无限字节(当然不是在循环中)就可以完成这项工作:
out.transferFrom(sChannel, out.position(), Integer.MAX_VALUE);
所以,我猜当套接字连接关闭时,状态将会改变,这将停止transferFrom方法。
但正如我已经说过的:我不确定。
建立在其他人所写的基础上,这里有一个简单的帮助方法可以实现目标:
public static void transferFully(FileChannel fileChannel, ReadableByteChannel sourceChannel, long totalSize) {
for (long bytesWritten = 0; bytesWritten < totalSize;) {
bytesWritten += fileChannel.transferFrom(sourceChannel, bytesWritten, totalSize - bytesWritten);
}
}
这边走:
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 大得多的计数开始,更像是一兆字节或两兆字节,否则您不会从该方法中获得太多好处。
编辑为了解决下面的所有评论,文档说“如果源通道剩余的字节数少于 count 个字节,或者源通道是非阻塞的,则传输的字节数将少于请求的字节数并且其输入缓冲区中立即可用的字节数少于 count 个字节。”因此,只要您处于阻塞模式,它就不会返回零,直到源中没有任何内容为止。因此循环直到返回零是有效的。
编辑2
传输方法肯定是设计错误的。它们应该被设计为在流末尾返回 -1,就像所有的 read() 方法一样。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
处理此案的方法很少。一些背景信息,说明 trasnferTo/From 在内部如何实现以及何时可以实现卓越。
FileChannel.trasnferTo(socketChanel)
发送文件v64。
简而言之
for (long xferBytes=0; startPos + xferBytes<="" code=""> 将适用于从文件传输 ->插座。
没有从套接字传输到文件的操作系统功能(OP 感兴趣)。由于套接字数据不在操作系统缓存中,因此无法有效完成,因此需要进行模拟。实现复制的最佳方法是通过标准循环,使用轮询的直接 ByteBuffer,其大小与套接字读取缓冲区相同。因为我只使用非阻塞 IO,也涉及选择器。
话虽这么说:我想让它与据称超级高效的“?一起工作 - 它效率不高,并且在所有操作系统上进行模拟,因此当套接字关闭时它将结束传输无论是否优雅,只要有任何传输(如果套接字可读且打开),该函数都不会抛出继承的 IOException。
我希望答案很明确:
File.transferFrom
当源是文件时发生。最有效(也是有趣的情况)是 file->socket 和 file->file 是通过filechanel.map
/取消映射(!!)。There are few ways to handle the case. Some background info how trasnferTo/From is implemented internally and when it can be superior.
FileChannel.size()
to determine the max available and sum the result. The case refers toFileChannel.trasnferTo(socketChanel)
sendfilev64
.in short
for (long xferBytes=0; startPos + xferBytes<fchannel.size();) doXfer()
will work for transfer from file -> socket.There is no OS function that transfers from socket to file (which the OP is interested in). Since the socket data is not int he OS cache it can't be done so effectively, it's emulated. The best way to implement the copy is via standard loop using a polled direct ByteBuffer sized with the socket read buffer. Since I use only non-blocking IO that involves a selector as well.
That being said: I'd like to get it working with the allegedly super efficient "? - it is not efficient and it's emulated on all OSes, hence it will end up the transfer when the socket is closed gracefully or not. The function will not even throw the inherited IOException, provided there was ANY transfer (If the socket was readable and open).
I hope the answer is clear: the only interesting use of
File.transferFrom
happens when the source is a file. The most efficient (and interesting case) is file->socket and file->file is implemented viafilechanel.map
/unmap(!!).