7 NIO编程

NIO是双向的,非阻塞,NIO面向缓冲区,效率高 ,有通道  

 

IO

NIO

面向流

面向缓冲区

阻塞IO

非阻塞IO

选择器(路由)

目录

Buffer

make(标记)和rest(重置)用法

直接缓冲区(最多用)与非直接缓冲

通道

分散读取和聚集写入

字符集 Charset

NIO同步阻塞和非同步阻塞

IO模型关系

NIO客户端和服务端(复杂不灵活)--Netty封装

 

 


Buffer

/**
 * 缓冲区 是NIO 提高给传输文件和通道一起使用,存储数据
 * Buffer
 * ByteBuffer(用的最多)
 * LongBuffer
 * FloatBuffer
 * DoubboBuffer
 */
import java.nio.ByteBuffer;
public class BuffTest {
    /** Buffer核心参数
     *  position :缓冲区正在操作的位置 默认从0开始
     *  limit;界面(缓冲区可用大小)
     *  capacity; 暖冲区最大容量,声明后不能改变
     *  核心方法:
     *  put():存放数据
     *  get()获取数据
     */
    @Test
        public void test001(){
        //初始化ByteBuffer大小
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        System.out.println(byteBuffer.position());//0
        System.out.println(byteBuffer.limit());//1024
        System.out.println(byteBuffer.capacity());//1024
        System.out.println("---------byteBuffer存入数据-------------");
        System.out.println();
        byteBuffer.put("abc1e".getBytes());
        System.out.println(byteBuffer.position());//5
        System.out.println(byteBuffer.limit());//1024
        System.out.println(byteBuffer.capacity());//1024
        System.out.println("--------byteBuffer读取值--------------");
        System.out.println();
        //开启读取模式
        byteBuffer.flip();
        System.out.println("position:::"+byteBuffer.position());//0
        System.out.println(byteBuffer.limit());//5
        System.out.println(byteBuffer.capacity());//1024
        byte[] bytes = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes);
        System.out.println(new String(bytes,0,bytes.length));
        System.out.println("---------byteBuffer重复读取-------------");
        byteBuffer.rewind();//重复读取上一次下标位置
        System.out.println("position:::"+byteBuffer.position());//0
        byte[] bytes2 = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes2);
        System.out.println(new String(bytes2,0,bytes2.length));
        //清空缓冲数据被遗忘值。。。
        System.out.println("----------清空缓冲区-------------");
        byteBuffer.clear();
        System.out.println("position:::"+byteBuffer.position());//0
        System.out.println(byteBuffer.limit());//1024
        System.out.println(byteBuffer.capacity());//1024
        System.out.println((char) byteBuffer.get());//a

    }
}

make(标记)和rest(重置)用法

        ByteBuffer byteBuffer = ByteBuffer.allocate(10);
        String str="abcd";
        byteBuffer.put(str.getBytes());
        //开启读的模式
        byteBuffer.flip();
        byte[] bytes = new byte[byteBuffer.limit()];
        byteBuffer.get(bytes,0,2);//取两个
        byteBuffer.mark();//打印标记
        System.out.println(new String(bytes,0,2));//ab
        System.out.println(byteBuffer.position());//2
        System.out.println("------------------");
        System.out.println(byteBuffer.position());//2
        byteBuffer.get(bytes,2,2);//取两个
        System.out.println(new String(bytes,2,2));//cd
        byteBuffer.reset();//还原到mark位置
        System.out.println("---重置还原标记---");
        System.out.println(byteBuffer.position());//2

直接缓冲区(最多用)与非直接缓冲

  直接缓冲区和非直接缓冲区(慢,安全)

非直接缓冲区:通过 allocate() 方法分配缓冲区,将缓冲区建立在 JVM 的内存中

 

直接缓冲区:通过 allocateDirect() 方法分配直接缓冲区,将缓冲区建立在物理内存中。可以提高效率

直接字节缓冲区还可以通过 FileChannel 的 map() 方法 将文件区域直接映射到内存中来创建。该方法返回MappedByteBuffer

每次调用基础操作系统的一个本机 I/O 操作之前(或之后),虚拟机都会尽量避免将缓冲区的内容复制到中间缓冲区中(或从中间缓冲区中复制内容)。

通道

java.nio.channels.Channel 接口:

           |--FileChannel

           |--SocketChannel

           |--ServerSocketChannel

           |--DatagramChannel

获取通道

  1. Java 针对支持通道的类提供了 getChannel() 方法

           本地 IO:

           FileInputStream/FileOutputStream

           RandomAccessFile

 

           网络IO:

           Socket

           ServerSocket

            DatagramSocket

          

  2. 在 JDK 1.7 中的 NIO.2 针对各个通道提供了静态方法 open()

  3. 在 JDK 1.7 中的 NIO.2 的 Files 工具类的 newByteChannel()

//非阻塞NIO
public class Test003 {
    //直接缓冲区(用的最多 下面为jdk1.7)
    @Test
    public void  test002() throws IOException {
        long starttimeMillis = System.currentTimeMillis();
        //创建管道
        FileChannel inChannel = FileChannel.open(Paths.get("src/main/resources/1.png"), StandardOpenOption.READ);
        FileChannel outChanel = FileChannel.open(Paths.get("src/main/resources/2.png"),StandardOpenOption.READ, StandardOpenOption.WRITE, StandardOpenOption.CREATE);
        //定义映射文件
        MappedByteBuffer inMappedByte = inChannel.map(FileChannel.MapMode.READ_ONLY, 0, inChannel.size());
        MappedByteBuffer outMappedByte = outChanel.map(FileChannel.MapMode.READ_WRITE, 0, inChannel.size());
        //直接对缓冲区操作
        byte[] dsf = new byte[inMappedByte.limit()];
        inMappedByte.get(dsf);
        outMappedByte.put(dsf);
        inChannel.close();
        outChanel.close();
        System.out.println("操作直接缓冲区完毕");
        long endtimeMillis = System.currentTimeMillis();
        System.out.println("-------直接缓冲区操作时间------");
        System.out.println(endtimeMillis-starttimeMillis);
    }

    //非直接缓冲区 读写操作
    @Test
    public void test001() throws IOException {
        System.out.println(new File(".").getAbsolutePath());
        long starttimeMillis = System.currentTimeMillis();
        //读入流
        FileInputStream fis = new FileInputStream("src/main/resources/1.png");
        //写入流
        FileOutputStream fos = new FileOutputStream("src/main/resources/2.png");
        //创建通道
        FileChannel inchannel = fis.getChannel();
        FileChannel outchannel = fos.getChannel();
        //分配指定大小缓冲区
        ByteBuffer buf = ByteBuffer.allocate(1024);
        while (inchannel.read(buf)!=-1){
            //开始读取模式
            buf.flip();
            //将数据写入到通道
            outchannel.write(buf);
            buf.clear();
        }
        //关闭通道
        inchannel.close();
        outchannel.close();
        fos.close();
        fis.close();
        long endtimeMillis = System.currentTimeMillis();
        System.out.println("-------非直接缓冲区操作时间------");
        System.out.println(endtimeMillis-starttimeMillis);
    }


}

分散读取和聚集写入

分散读取(scattering Reads):将通道中的数据分散到多个缓冲区中

聚集写入(gathering Writes):将多个缓冲区的数据聚集到通道中

import java.io.IOException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

public class Test004 {
    public static void main(String[] args) throws IOException {
        //随机访问
        RandomAccessFile raf = new RandomAccessFile("F:/mywork/Json_demo/src/main/resources/test.txt","rw");
        //获取通道
        FileChannel channel = raf.getChannel();//jdk 1.4
        //分配指定大小缓冲区
        ByteBuffer buf1 = ByteBuffer.allocate(100);
        ByteBuffer buf2 = ByteBuffer.allocate(1024);
        //分散读取
      ByteBuffer[] bufs= {buf1,buf2};
      channel.read(bufs);
        for (ByteBuffer byteBuffer:bufs) {
            //切换成读模式
            byteBuffer.flip();
        }
        System.out.println(new String(bufs[0].array(),0,bufs[0].limit()));
        System.out.println("*********************************************");
        System.out.println(new String(bufs[1].array(),1,bufs[1].limit()));
        System.out.println("---------聚集读取 --------");
        // 聚集写入
        RandomAccessFile raf2 = new RandomAccessFile("F:/mywork/Json_demo/src/main/resources/test2.txt","rw");
        //获取通道
        FileChannel channel2 = raf2.getChannel();//jdk 1.4
        channel2.write(bufs);
        raf2.close();
        raf.close();


    }
}

字符集 Charset

编码:字符串->字节数组

解码:字节数组 -> 字符串

public class Test005 {
    public static void main(String[] args) throws CharacterCodingException {
        //获取编码器
        Charset  charset= Charset.forName("GBK");
        //获取加密器
        CharsetEncoder ce = charset.newEncoder();
        //获取解码器
        CharsetDecoder cd = charset.newDecoder();
        CharBuffer charBuffer = CharBuffer.allocate(1024);
        charBuffer.put("编码棒棒棒");
        charBuffer.flip();
        //编码加密
        ByteBuffer buffer = ce.encode(charBuffer);
        for (int i = 0; i < 20; i++) {
            System.out.println(buffer.get());
            
        }
        //开启读取模式
        buffer.flip();
        //编码解密
        CharBuffer decode = cd.decode(buffer);
        System.out.println(decode.toString());


    }
}

NIO同步阻塞和非同步阻塞

非阻塞IO:NIO(同步,jdk1.7之前);AIO(异步),只存在网络上

阻塞IO:IO(BIO)同步

伪异步=多线程

伪异步不靠谱:

IO模型关系

NIO客户端和服务端(复杂不灵活)--Netty封装

选择KEY

1、SelectionKey.OP_CONNECT 可连接

2、SelectionKey.OP_ACCEPT 可接受连接

3、SelectionKey.OP_READ 可读

4、SelectionKey.OP_WRITE 可写

如果你对不止一种事件感兴趣,那么可以用“位或”操作符将常量连接起来,如下:

int interestSet = SelectionKey.OP_READ | SelectionKey.OP_WRITE;

在SelectionKey类的源码中我们可以看到如下的4中属性,四个变量用来表示四种不同类型的事件:可读、可写、可连接、可接受连接

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Date;
import java.util.Iterator;

//NIO客户端 
class NioClient{
    public static void main(String[] args) throws IOException {
        System.out.println("-----客户端已启动------");
        //1创建socket通道
        SocketChannel socketChannel = SocketChannel.open(new InetSocketAddress("127.0.0.1",8080));
       //2切换异步非阻塞
        socketChannel.configureBlocking(false);//jdk1.7以上
        //3指定缓冲区大小
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
        byteBuffer.put(new Date().toString().getBytes());
        //4切换到读取模式
        byteBuffer.flip();
        socketChannel.write(byteBuffer);
        byteBuffer.clear();
        //5关闭通道
        socketChannel.close();
    }
}
//NIO服务器端
 class  NIOService{
    public static void main(String[] args) throws IOException {
        System.out.println("服务器端启动-----");
        //1创建通道
        ServerSocketChannel sChannel = ServerSocketChannel.open();
        //2切换异步非阻塞
        sChannel.configureBlocking(false);//jdk 1.7以上
        //3绑定连接
        sChannel.bind(new InetSocketAddress(8080));
        //4获取选择器
        Selector selector = Selector.open();
        //5将通道注册到选择器中 并且监听已经接受的事件
        sChannel.register(selector, SelectionKey.OP_ACCEPT);
       //6轮询获取”已经准备就绪的“事件
        while (selector.select()>0){
            Iterator<SelectionKey> it = selector.selectedKeys().iterator();
            while (it.hasNext()){
                //8获取准备就绪事件
                SelectionKey sk = it.next();
                //9判断事件准备事件
                if (sk.isAcceptable()){
                    //10若接受就绪,获取客户端连接
                    SocketChannel socketChannel = sChannel.accept();
                    //11切换异步非阻塞
                    socketChannel.configureBlocking(false);//jdk 1.7以上
                    //12将通道注册到服务器上
                    socketChannel.register(selector,SelectionKey.OP_READ);
                }else if (sk.isReadable()){
                    //13获取当前选择”就绪状态的通道“
                    SocketChannel socketChannel =(SocketChannel) sk.channel();
                    //14读取
                    int len=0;
                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                    while ((len=socketChannel.read(byteBuffer))>0){
                        byteBuffer.flip();
                        System.out.println("写入时数据:::"+new String(byteBuffer.array(),0,len));
                        byteBuffer.clear();
                    }
                }
                it.remove();
            }

        }


    }

 }
public class Client {
}

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值