测试环境

  • 两台RedHat6.5 64位虚拟机
  • JDK版本:1.7.0_21
  • 测试文件大小:212MB

NIO发送测试

1.服务端代码


public static void main(String[] args) { if(args.length != 1){ System.out.println("Usage : java -jar ServerTcpListener.jar port"); return; } try { final ServerSocketChannel serverSocketChannel = ServerSocketChannel.open(); serverSocketChannel.socket().bind(new InetSocketAddress(Integer.parseInt(args[0]))); Thread th = new Thread(new Runnable() { @Override public void run() { try { serverSocketChannel.configureBlocking(false); while(true){ SocketChannel socketChannel = serverSocketChannel.accept(); if(socketChannel != null){ receiveFile(socketChannel); } } } catch (IOException e) { e.printStackTrace(); } } }); th.run(); } catch (Exception ex) { ex.printStackTrace(); } } public static void receiveFile(SocketChannel socketChannel) throws IOException { final Path filePath = Paths.get("test"); //要将接收的文件写到当前目录的test文件中 FileChannel fileChannel = (FileChannel.open(filePath, EnumSet.of(StandardOpenOption.CREATE_NEW, StandardOpenOption.WRITE, StandardOpenOption.TRUNCATE_EXISTING))); //先获取文件大小,这里我约定前8个字节表示文件大小 ByteBuffer buf = ByteBuffer.allocate(8); socketChannel.read(buf); buf.flip(); long fileSize = buf.getLong(); System.out.println("fileSize :" + fileSize); //接收文件内容 fileChannel.transferFrom(socketChannel, 0, fileSize); fileChannel.close(); }

2.客户端代码

public static void main(String[] args) {

        if(args.length != 3){
            System.out.println("Usage :  java -jar ClientTcpSend.jar ipaddress port  filename");
            return;
        }

        SocketChannel socketChannel;
        try {
            socketChannel = SocketChannel.open();
            socketChannel.connect(new InetSocketAddress(args [0], Integer.parseInt(args[1])));

            final Path filePath = Paths.get(args[2]);

            FileChannel fileChannel = (FileChannel.open(filePath, 
                    EnumSet.of(StandardOpenOption.READ)));

            long start = System.currentTimeMillis();

            //用来发送文件大小
            ByteBuffer buf = ByteBuffer.allocate(8);
            buf.asLongBuffer().put(fileChannel.size());
            socketChannel.write(buf);

            //发送文件内容
            fileChannel.transferTo(0, fileChannel.size(), socketChannel);
            fileChannel.close();
            socketChannel.close();

            long end = System.currentTimeMillis();

            System.out.println("Total use " + (end - start) + "ms");

        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }


    }
}

3.测试过程

将两个jar包传到不同的服务器中,在服务端启动监听
java -jar NIOServer.jar 12345
在客户端向服务端发送文件,如下:
java -jar java -jar NIOClient.jar 192.168.11.102 12345 spark-1.3.0-cdh5.4.4.tar.gz

4.测试结果

服务器端

客户端

Read/Write发送测试

1.测试过程

使用TCP建立socket连接,然后从客户端向服务端发送。代码太简单就不粘贴了。

2.测试结果

客户端

服务端:略

总结

由上述测试可以看到NIO与Read/Write在网络传输文件过程中的差异。NIO消除了CPU的数据拷贝,直接在内核空间中,将数据从文件缓冲区发送到网卡缓冲区中进行发送。而传统模式下数据从文件传输到网络需要4次数据拷贝,4次上下文切换和2次系统调用。如下:

传统模式下发送文件过程

NIO transferTo/transferFrom发送文件过程

Java NIO的transferTo/transferFrom与read/write方式文件网络数据传输测试对比
Tagged on:             

2 thoughts on “Java NIO的transferTo/transferFrom与read/write方式文件网络数据传输测试对比

  • 2018年1月29日 at 下午4:22
    Permalink

    楼主采用的方法有点问题。在客户端发送文件时,transferTo方法不应该传入fileChannel.size(),默认只能传8192字节即8KB,得采用循环发送的方式才能完整传输文件。
    原来:
    fileChannel.transferTo(0, fileChannel.size(), socketChannel);
    修改后:
    long position = 0;
    long maxCount = 8192;
    while (position < fileChannel.size()) {
    position += fileChannel.transferTo(position, maxCount, socketChannel);
    }

    Reply
    • 2018年1月31日 at 下午4:03
      Permalink

      感谢提示,fileChannel默认缓冲区是8192,使用你提供的这种方式应该是比较稳妥的一种方式,不过我这边测试过程汇总,直接使用fileChannel.size()也发送成功了,接下来我仔细研究一下。

      Reply

adamfei进行回复 取消回复

您的电子邮箱地址不会被公开。 必填项已用*标注

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据