【问题标题】:Incomplete File Copy Java NIO不完整的文件复制 Java NIO
【发布时间】:2016-08-21 15:52:08
【问题描述】:

我在读取文件时遇到问题。我对 NIO 也很陌生。我要发送到服务器的文件的实际大小几乎是 900MB 并且只收到了 3MB

服务器端读取代码:

private void read(SelectionKey key) throws IOException{
    SocketChannel socket = (SocketChannel)key.channel();
    RandomAccessFile aFile = null;
    ByteBuffer buffer = ByteBuffer.allocate(300000000);
    try{
        aFile = new RandomAccessFile("D:/test2/test.rar","rw");

        FileChannel inChannel = aFile.getChannel();

        while(socket.read(buffer) > 0){
            buffer.flip();
            inChannel.write(buffer);
            buffer.compact();
        }
        System.out.println("End of file reached..");
    }catch(Exception e){
        e.printStackTrace();
    }
}

这是我的客户端写方法的代码:

private void write(SelectionKey key) throws IOException {
    SocketChannel socket = (SocketChannel) key.channel();
    RandomAccessFile aFile = null;
    try {
        File f = new File("D:/test.rar");
        aFile = new RandomAccessFile(f, "r");
        ByteBuffer buffer = ByteBuffer.allocate(300000000);

        FileChannel inChannel = aFile.getChannel();
        while (inChannel.read(buffer) > 0) {
            buffer.flip();
            socket.write(buffer);
            buffer.compact();
        }
        aFile.close();
        inChannel.close();

        key.interestOps(SelectionKey.OP_READ);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

【问题讨论】:

  • 想一想:read() 通常会告诉您已读取的确切字节数。这并不意味着读取了所有缓冲区字节。至少在老式 IO 中,您必须循环直到从缓冲区中读取所有字节。
  • socket.read(buffer) > 0 + non-blocking IO = failure,因为零并不意味着流的结束,只是没有准备好读取的数据。因此,您在处理 NIO 时以完全阻塞的方式阅读,这显然行不通。

标签: java asynchronous nio filechannel


【解决方案1】:
  1. 每次套接字通道变得可读时,您都会打开一个新文件。每个到达的 TCP 段,您都在重新创建目标文件,因此丢弃之前收到的任何内容。

    解决这个问题的简单方法是在每个 OP_READ 上打开文件以进行追加,但它的效率仍然非常低下。您应该在知道它是什么后立即打开目标文件,并在从发送方读取流结束时关闭它,或者当您读取整个内容时(如果流结束没有发出信号)。你还没有透露你的应用协议,所以我不能更具体。

  2. read() 在没有数据可供读取时返回零。您将其视为文件结尾。不是。
  3. 通道之间写入的规范方式如下:

    while ((in.read(buffer) > 0 || buffer.position() > 0)
    {
        buffer.flip();
        out.write(buffer);
        buffer.compact();
    }
    

    但是,如果目标是非阻塞套接字通道,这将变得相当复杂:您必须根据最后一个 write() 是否返回零来控制是否选择了 OP_WRITE。你会发现这里有很多帖子解释了这一点,其中很多都是我写的。

  4. 我从未见过客户端非阻塞 I/O 的任何令人信服的理由,除非它连接到多个服务器(例如网络爬虫)。我会在客户端使用阻塞模式或java.net.Socket,这样可以避免上面提到的写入复杂性。

注意,您不需要关闭派生自它的 RandomAccessFileFileChannel。两者都行。

【讨论】:

  • 那么如果是非阻塞套接字通道,最好的写法是什么?
  • 带有一个阻塞套接字通道,或者一个java.net.Socket。我是这么说的。
猜你喜欢
  • 2016-01-18
  • 2018-07-28
  • 1970-01-01
  • 1970-01-01
  • 2021-06-07
  • 1970-01-01
  • 2017-10-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多