Java NIO:从流到流结束的传输

2022-09-02 21:19:36

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

我知道如何使用 它,但我想让它与据称超高效的FileChannel.transferFrom一起工作。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();

所以,我的问题是:我如何表达“某个频道转移到到达流结束”


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


答案 1

处理此案的方法很少。一些背景信息,说明如何在内部实现 trasnferTo/From,以及何时可以更高级。

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

简而言之,将适用于从文件->套接字传输。没有操作系统功能可以从套接字传输到文件(OP对此感兴趣)。由于套接字数据不在操作系统缓存中,因此无法有效地完成,因此会进行模拟。实现复制的最佳方法是通过标准循环,使用轮询的直接字节缓冲区大小和套接字读取缓冲区。由于我只使用涉及选择器的非阻塞IO。for (long xferBytes=0; startPos + xferBytes<fchannel.size();) doXfer()

话虽这么说:我想让它与所谓的超高效“一起工作”?- 它效率不高,并且在所有操作系统上都模拟,因此当套接字正常关闭或不正常关闭时,它最终将结束传输。该函数甚至不会抛出继承的IOException,前提是有任何传输(如果套接字是可读的并且打开的)。

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


答案 2

直接回答您的问题:

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

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

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

看看本教程:http://rox-xmlrpc.sourceforge.net/niotut/ 我相信它将帮助您理解问题。


推荐