NIO zero-copy file transfer case (with available code)

Zero copy has much higher performance than traditional IO copy, mainly because it reduces the number of context switches between kernel and user mode. Zero copy is completely dependent on the operating system. Operating system support, there will be; if not, there will be no. Do not rely on Java itself.

Traditional I/O

In Java, we can read the data stream from the source data into a buffer through InputStream, and then input them into the OutputStream. We know that the transmission efficiency of this IO method is relatively low. So, what happens to the operating system when the above code is used:

This is a process of reading from a disk file and writing out through a socket. The corresponding system call is as follows:

 

read(file,tmp_buf,len)
write(socket,tmp_buf,len)

 

  1. The program uses the read() system call. The system changes from user mode to kernel mode (the first online text switching), and the data in the disk is read into the kernel buffer by means of DMA (Direct Memory Access). In the DMA process, the CPU does not need to participate in the reading and writing of data, but the DMA processor directly transfers the hard disk data to the memory through the bus.
  2. The system changes from the kernel mode to the user mode (the second context switch). After the data to be read by the program has been written into the kernel buffer, the program will write the data from the kernel buffer to the user buffer). This process The CPU is required to participate in the reading and writing of data.
  3. The program uses the write() system call. The system switches from user mode to kernel mode (the third context switch), and data is written from the user mode buffer to the network buffer (Socket Buffer). This process requires the CPU to participate in data reading and writing.
  4. The system switches from the kernel mode to the user mode (the fourth context switch), and the data in the network buffer is transferred to the driver (storage buffer) of the network card through DMA (protocol engine)

The case is to transfer data from a file, as shown in the figure is to transfer data from CodeForces.rar.

 

The size of the transmitted file is shown in the figure: 19130 bytes

Client NewIOClient.java:

import java.io.FileInputStream;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.FileChannel;
import java.nio.channels.SocketChannel;

public class NewIOClient {
    public static void main(String[] args) throws IOException {
        SocketChannel socketChannel = SocketChannel.open();
        socketChannel.connect(new InetSocketAddress("localhost",4399));
        String filename="CodeForces.rar";

        //得到文件的channel
        FileChannel fileChannel = new FileInputStream(filename).getChannel();

        long startTimeMillis = System.currentTimeMillis();

        //在Linux下一个transto函数可以完成传输
        //在windows下一次调用只能发送8M的文件,需要分段传输文件,而且要注意传送时的位置
        //transferto底层用到零拷贝,传送到socketchannel中
        long transfercount = fileChannel.transferTo(0, fileChannel.size(), socketChannel);
        System.out.println("发送的字节数为:"+transfercount+",耗时为:" + (System.currentTimeMillis() - startTimeMillis));
        fileChannel.close();

    }
}

Server NewIOServer.java:

import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.ServerSocket;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;

public class NewIOServer {
    public static void main(String[] args) throws IOException {
        InetSocketAddress inetSocketAddress = new InetSocketAddress(4399);
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        ServerSocket serverSocket = serverSocketChannel.socket();
        serverSocket.bind(inetSocketAddress);

        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while (true){
            SocketChannel socketChannel = serverSocketChannel.accept();
            int readacount=0;
            while (-1!=readacount){
                try {
                    readacount = socketChannel.read(byteBuffer);
                }catch (Exception e){
                    break;
                }
                byteBuffer.rewind();    //将buffer的position置为0,mark作废
            }
        }
    }
}

Run the server code first, and then run the client, and found that it only took 7ms, while the traditional IO needs at least 20ms. It can be seen that the zero copy method is very cost-effective.

Guess you like

Origin blog.csdn.net/Zhongtongyi/article/details/107490488