JavaNIO:从传输到流结束

JavaNIO:从传输到流结束,java,nio,filechannel,socketchannel,Java,Nio,Filechannel,Socketchannel,我在玩NIO图书馆。我正在尝试侦听8888端口上的连接,一旦连接被接受,就将该通道中的所有内容转储到somefile 我知道如何使用ByteBuffers,但我想让它使用据称超高效的工具 这就是我得到的: ServerSocketChannel ssChannel = ServerSocketChannel.open(); ssChannel.socket().bind(new InetSocketAddress(8888)); SocketChannel sChannel = ssChann

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

我知道如何使用
ByteBuffers
,但我想让它使用据称超高效的工具

这就是我得到的:

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();
ServerSocketChannel ssChannel=ServerSocketChannel.open();
ssChannel.socket().bind(新的InetSocketAddress(8888));
SocketChannel sChannel=ssChannel.accept();
FileChannel out=newfileoutputstream(“somefile”).getChannel();

虽然(…sChannel还没有到达流的尽头…我不确定,但是JavaDoc说:

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

我想你可能会说,告诉它复制无限字节(当然不是在循环中)就可以了:

out.transferFrom(sChannel, out.position(), Integer.MAX_VALUE);
因此,我猜当套接字连接关闭时,状态将发生更改,这将停止
transferFrom
方法


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

直接回答你的问题:

while( (count = socketChannel.read(this.readBuffer) )  >= 0) {
   /// do something
}
但是,如果这是您所做的,您就不会使用非阻塞IO的任何好处,因为您实际上将其完全用作阻塞IO。非阻塞IO的要点是,一个网络线程可以同时为多个客户端提供服务:如果一个通道没有任何内容可读取(即,
count==0
),则可以切换到其他通道(属于其他客户端连接)

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

请看一下本教程: 我相信这会帮助你理解这个问题

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

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

编辑2

传输方法肯定是设计错误的。它们应该被设计为在流的末尾返回-1,就像所有
read()
方法一样

据称超级高效的FileChannel.transferFrom

如果您想同时获得DMA访问和非阻塞IO的好处,最好的方法是将文件映射到内存,然后从套接字读取到内存映射的缓冲区


但这需要预先分配文件。

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

  • 首先也是最重要的一点是,您应该知道必须输入多少字节,即使用
    FileChannel.size()
    确定最大可用字节数并对结果求和。案例涉及
    FileChannel.trasnferTo(socketChanel)
  • 该方法不返回-1
  • 该方法在Windows上进行了仿真。Windows没有从filedescriptor转换到socket的API函数,它有一(两)个从名称指定的文件转换到socket的函数,但这与java API不兼容
  • 在Linux上使用标准(或sendfile64),在Solaris上称为
    sendfilev64
简而言之,
for(长xferBytes=0;启动TPOS+xferBytes套接字)。
没有从套接字传输到文件(OP感兴趣)的操作系统函数。由于套接字数据不在操作系统缓存中,因此无法有效地执行此操作,因此对其进行了模拟。实现复制的最佳方法是通过标准循环,使用大小与套接字读取缓冲区相同的轮询直接字节缓冲区。因为我只使用了包含选择器的非阻塞IO

这就是说:我想让它与所谓的超级高效“?-它不是高效的,并且在所有操作系统上都是模拟的,因此无论套接字是否正常关闭,它都会在传输结束时结束。如果有任何传输(如果套接字可读且打开),函数甚至不会抛出继承的IOException

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

以下方式实现:

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

在这里的其他人所写的基础上,这里有一个简单的helper方法,可以实现以下目标:

public static void transferFully(FileChannel fileChannel, ReadableByteChannel sourceChannel, long totalSize) {
    for (long bytesWritten = 0; bytesWritten < totalSize;) {
        bytesWritten += fileChannel.transferFrom(sourceChannel, bytesWritten, totalSize - bytesWritten);
    }
}
public static void transferFully(FileChannel FileChannel,ReadableByteChannel sourceChannel,long totalSize){
for(长字节写入=0;字节写入
你比我强。但是你可以使用
Long.MAX\u VALUE
而不是
Integer.MAX\u VALUE
。对,但是如果我在第一次调用transferFrom时只得到10个字节中的3个字节呢?也许可以测试你的源通道:它有一个方法。如果返回
-1
t