Java nio基本知识1:channel和buffer

本文深入探讨了阻塞IO与非阻塞IO的工作原理及应用,详细介绍了NIO的核心组件如Channels、Buffers与Selectors的使用方法,并通过实例展示了如何利用FileChannel进行文件操作,以及SocketChannel、DatagramChannel在TCP和UDP通信中的应用。

阻塞IO

读写时候阻塞,每个请求是一个线程

非阻塞IO

基于Reactor工作模式,IO不会阻塞,注册特定IO事件,发生特定事件时候系统会通知。核心对象是Selector,
读、写、注册发生时候selector可以找到发生事件的Selector Channel,获取客户端数据。

非阻塞IO事件本身不阻塞,但是获取事件的select()方法是阻塞的,本质是发生io时候,而不是以前只要IO流打开就一直等待。

NIO

核心组件:Channels Buffers Selectors

filechannel

写入文件小例子

public class io1 {
    public static void main(String[] args) throws IOException {
        File f = new File("C:\\Users\\86158\\Desktop\\datax.txt");
        RandomAccessFile ra = new RandomAccessFile(f,"rw");
        FileChannel fc = ra.getChannel();

        //创建buffer
        ByteBuffer bf = ByteBuffer.allocate(1024);

        String input = "abcdefg";

        bf.clear();
        bf.put(input.getBytes());
        bf.flip();

        while (bf.hasRemaining()){
            fc.write(bf);
        }
        fc.close();
    }
}

FileChannel
position在某些特定位置进行读写
size返回文件大小
truncate 截取字节
force 强制写入
transferTo/transferFrom 通道间数据传输

通道间数据传输小例子

public class io2 {
    public static void main(String[] args) throws IOException {
        File f = new File("C:\\Users\\86158\\Desktop\\datax.txt");
        File f2 = new File("C:\\Users\\86158\\Desktop\\datax2.txt");
        RandomAccessFile ra1 = new RandomAccessFile(f,"rw");
        RandomAccessFile ra2 = new RandomAccessFile(f2,"rw");
        FileChannel fc1 = ra1.getChannel();
        FileChannel fc2 = ra1.getChannel();

        //fc1数据传入fc2
        fc1.transferTo(0,fc1.size(),fc2);

    }
}


socketChannel

serverSocketChannel可以监听新进来的TCP连接的通道

监听8888端口

public class io3 {
    public static void main(String[] args) throws IOException, InterruptedException {
        int port = 8888;
        ByteBuffer bf = ByteBuffer.wrap("hello".getBytes());

        ServerSocketChannel ssc = ServerSocketChannel.open();
        ssc.socket().bind(new InetSocketAddress(port));

        //设置非阻塞
        ssc.configureBlocking(false);

        //监听新链接传入
        while (true){
            SocketChannel sc = ssc.accept();
            if(sc == null){
                Thread.sleep(2000);
            }else {
                System.out.println(sc.socket().getRemoteSocketAddress());
                bf.rewind();
                sc.write(bf);
                sc.close();
            }

        }
    }
}

socketChannel
面向TCP

public class io4 {
    public static void main(String[] args) throws IOException {
        SocketChannel sc = SocketChannel.open(new InetSocketAddress("www.baidu.com",80));

        sc.configureBlocking(false);
        ByteBuffer bf = ByteBuffer.allocate(100);
        sc.read(bf);
        sc.close();
        System.out.println("over");


    }
}

datagramChannel
面向UDP

public class io5 {

    public  void send() throws IOException, InterruptedException {
        DatagramChannel dc = DatagramChannel.open();
        InetSocketAddress ad = new InetSocketAddress("127.0.0.1",9999);
        while (true){
            ByteBuffer bf = ByteBuffer.wrap("jsjsjsj".getBytes());
            dc.send(bf,ad);
            System.out.println("发送");
            Thread.sleep(1000);
        }

    }

    public void receive() throws IOException {
        DatagramChannel dc = DatagramChannel.open();
        InetSocketAddress ad = new InetSocketAddress(9999);
        dc.bind(ad);
        ByteBuffer bf = ByteBuffer.allocate(1024);
        while (true){
            bf.clear();
            SocketAddress sc = dc.receive(bf);
            bf.flip();

            System.out.println(sc.toString() + bf);
        }



    }
}

channel的scatter和gather功能
在这里插入图片描述

在这里插入图片描述

Buffer

就是一块可以读写的内存空间,NIO中所有数据都用缓冲区处理

两种分配Buffer的方法:
ByteBuffer buf = ByteBuffer.allocate(10);
在堆中开辟,易于管理,垃圾回收器可以回收,空间有限,读写慢。

ByteBuffer buf2=ByteBuffer.allocateDirect(10);
物理内存中开辟空间,空间比较大,读写文件速度快,不受垃圾回收器控制。

buffer读写操作

public class io6 {
    public static void main(String[] args) throws IOException {
        buffer01();
    }

    public static void buffer01() throws IOException {
        RandomAccessFile ra = new RandomAccessFile("C:\\Users\\86158\\Desktop\\datax.txt","rw");
        FileChannel channel = ra.getChannel();
        ByteBuffer bf = ByteBuffer.allocate(1024);

        //读
        channel.read(bf);
        bf.flip();
        while (bf.hasRemaining()){
            System.out.println((char) bf.get());
        }
        bf.clear();


        //写
        for (int ii = 0; ii < bf.capacity();ii++){
            bf.put((byte) ii);
        }
        bf.flip();
        while (bf.hasRemaining()){
            System.out.println((char) bf.get());
        }
        bf.clear();

    }
}

buffer的属性 capacity position limit

capacity: 最多能写多少

position: 读写的当前位置,flip后变0

limit : 最多读到哪里(flip前的position)/最多写到哪里(capacity)

buffer放入数据两个方式

buffer.put()
channel.read(buffer)

buffer读数据
buffer.get()
channel.write(buffer)

rewind方法/flip方法

flip是写好了开始读,limit 要设成position,position变0

rewind是单纯position变0

clear方法/compact方法

clear: position设0
compact:准备好写数据了,未读过的数据不清除,未读数据拷贝到buffer起始处,position设为最后一个未读数据后面

mark方法/reset方法

mark标记特定position, reset恢复到这个position

buffer分片

在 NIO 中,除了可以分配或者包装一个缓冲区对象外,还可以根据现有的缓冲区对象
来创建一个子缓冲区,即在现有缓冲区上切出一片来作为一个新的缓冲区,但现有的
缓冲区与创建的子缓冲区在底层数组层面上是数据共享的,也就是说,子缓冲区相当
于是现有缓冲区的一个视图窗口。调用 slice()方法可以创建一个子缓冲区。

切片范围是调用slice方法时原始缓冲区的position到limit索引之间的数据

buffer只读

只读缓冲区非常简单,可以读取它们,但是不能向它们写入数据。可以通过调用缓冲
区的 asReadOnlyBuffer()方法,将任何常规缓冲区转 换为只读缓冲区,这个方法返
回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。
如果原缓冲区的内容发生了变化,只读缓冲区的内容也随之发生变化

区相当
于是现有缓冲区的一个视图窗口。调用 slice()方法可以创建一个子缓冲区。

切片范围是调用slice方法时原始缓冲区的position到limit索引之间的数据

buffer只读

只读缓冲区非常简单,可以读取它们,但是不能向它们写入数据。可以通过调用缓冲
区的 asReadOnlyBuffer()方法,将任何常规缓冲区转 换为只读缓冲区,这个方法返
回一个与原缓冲区完全相同的缓冲区,并与原缓冲区共享数据,只不过它是只读的。
如果原缓冲区的内容发生了变化,只读缓冲区的内容也随之发生变化

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值