Java NIO——channel

1 概述

channel,意为通道,表示IO流和目标打开的连接。channel本身不能直接访问数据,只能与Buffer进行交互。所有的数据都是通过Buffer进行交互的。
在这里插入图片描述

  • 读:将数据从channel通道读入缓冲区,再从缓冲区获取这个字节。
  • 写:将数据写入Buffer,将buffer中数据写入channel。

NIO包中有channel的几种实现

  • FileChannel:用于读取、写入、映射和操作文件的通道。
  • DatagramChannel:能通过UDP读写网络中的数据。
  • SocketChannel:能通过TCP读写网络中的数据。
  • ServerSocketChannel:可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

2 FileChannel

I/O(asynchronous I/O),允许一个进程可以从操作系统请求一个或多个I/O操作而不必等待这些操作的完成。发起请求的进程之后会收到它请求的I/O操作已完成的通知。

2.1 getChannel

需要通过使用inputStream,outputStream或者RandomAccessFile来获取实例

FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();

2.2 Scatter和Gatter

分散读取(Scattering Reads)是指从 Channel 中读取的数据“分散” 到多个 Buffer 中。按照缓冲区的顺序,从 Channel 中读取的数据依次将 Buffer 填满。

聚集写入(Gathering Writes)是指将多个 Buffer 中的数据“聚集”
到 Channel。
scatter / gather经常用于需要将传输的数据分开处理的场合
Scattering Reads在移动下一个buffer前,必须填满当前的buffer,这也意味着它不适用于动态消息(译者注:消息大小不固定)。换句话说,如果存在消息头和消息体,消息头必须完成填充(例如 128byte),Scattering Reads才能正常工。
Gatter writer会按照buffer在数组中的顺序,将数据写入到channel,注意只有position和limit之间的数据才会被写入。因此,如果一个buffer的容量为128byte,但是仅仅包含58byte的数据,那么这58byte的数据将被写入到channel中。因此与Scattering Reads相反,Gathering Writes能较好的处理动态消息。

//Gatter
ByteBuffer header = ByteBuffer.allocate(128);
ByteBuffer body   = ByteBuffer.allocate(1024);
//write data into buffers
ByteBuffer[] bufferArray = { header, body };
channel.write(bufferArray);

2.3 常用方法

int read(ByteBuffer dst)//从channel中读取数据到ByteBuffer
long read(ByterBuffer dsts)//从channel中“分散”数据到Bytebuffer[]中
int write(ByteBuffer src)将ByteBuffer中数据写入Channel
long write(ByteBuffer[] srcs)将ByteBuffer中数据“聚集”到Channel
transferFrom(Channel from,long count,long pos)
transferTo(Channel to,long count,long pos)

2.4 demo

 @Test
    public void testCostTime() throws IOException {
        String src = "D:\\mygit\\downloads\\2016年初评成果附件.zip";
        String des = "D:\\mygit\\downloads\\copy.zip";
        Path from = Paths.get(src);
        Path to = Paths.get(des);
        long start1 = System.currentTimeMillis();
        Files.copy(from,to,NOFOLLOW_LINKS);
        long cost1 = System.currentTimeMillis();
        System.out.println(cost1-start1);

        long start2 = System.currentTimeMillis();
        copyByChannel(src,des);
        long cost2 = System.currentTimeMillis();
        System.out.println(cost2-start2);

        long start3 = System.currentTimeMillis();
        copyByNioBuffer2(src,des);
        long cost3 = System.currentTimeMillis();
        System.out.println(cost3-start3);
        
        long start4 = System.currentTimeMillis();
        copyByNioDirectBuffer(src,des);
        long cost4 = System.currentTimeMillis();
        System.out.println(cost4-start4);
    }
    public void copyByChannel(String src,String des) throws IOException {
        RandomAccessFile sr = new RandomAccessFile(src,"r");
        RandomAccessFile dr = new RandomAccessFile(des,"rw");
        FileChannel schannel = sr.getChannel();
        FileChannel dchannel = dr.getChannel();
        schannel.transferTo(0L,schannel.size(), dchannel);
    }

    public void copyByNioBuffer(String src,String des) throws IOException {
        RandomAccessFile sr = new RandomAccessFile(src,"r");
        RandomAccessFile dr = new RandomAccessFile(des,"rw");
        FileChannel schannel = sr.getChannel();
        FileChannel dchannel = dr.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        while ((schannel.read(byteBuffer)) !=-1){
            byteBuffer.flip();
            dchannel.write(byteBuffer);
            byteBuffer.clear();
        }
        schannel.close();
        dchannel.close();
    }
    
    public void copyByNioDirectBuffer(String src,String des) throws IOException {
        RandomAccessFile sr = new RandomAccessFile(src,"r");
        RandomAccessFile dr = new RandomAccessFile(des,"rw");
        FileChannel schannel = sr.getChannel();
        FileChannel dchannel = dr.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocateDirect(1024);
        while ((schannel.read(byteBuffer)) !=-1){
            byteBuffer.flip();
            dchannel.write(byteBuffer);
            byteBuffer.clear();
        }
        schannel.close();
        dchannel.close();
    }
// 文件大小为1.4G
// 当增大缓存区大小时,DirectBuffer可能比Buffer快
5127 //Files.copy
10261 // transferTo
7330 // copyByNioBuffer
15681 //copyByNioDirectBuffer

3 DatagramChannel

// 打开dataGramChannel
DatagramChannel channel = DatagramChannel.open();
channel.socket().bind(new InetSocketAddress(9999));
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
// 接收数据
channel.receive(buf);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
// 发送数据
channel.send(buf);
channel.close();
channel.connect(new InetSocketAddress("jenkov.com", 80));

4 SocketChannel

SocketChannel是一个连接到TCP网络套接字的通道。
操作步骤:

  • 打开 SocketChannel
  • 读写数据
  • 关闭 SocketChannel
//打开SocketChannel
SocketChannel socketChannel = SocketChannel.open();
socketChannel.connect(new InetSocketAddress("http://jenkov.com", 80));
// 写入数据
String newData = "New String to write to file..." + System.currentTimeMillis();
ByteBuffer buf = ByteBuffer.allocate(48);
buf.clear();
buf.put(newData.getBytes());
buf.flip();
while(buf.hasRemaining()) {
   socketChannel.write(buf);
}
// 读取数据
ByteBuffer buf = ByteBuffer.allocate(48);
int bytesRead = socketChannel.read(buf)
// 关闭socketChannel
socketChannel.close();

非阻塞模式
将scoketChannel设为非阻塞模式,read和write方法会立即返回,可以异步调用。

serverSocketChannel.configureBlocking(false);

5 ServerSocketChannel

ServerSocketChannel 是一个可以监听新进来的TCP连接的通道, 就像标准IO中的ServerSocket一样。

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
serverSocketChannel.socket().bind(new InetSocketAddress(9999));
while(true){
    SocketChannel socketChannel =
            serverSocketChannel.accept();
    //do something with socketChannel...
}

猜你喜欢

转载自blog.csdn.net/ccoran/article/details/84849406