Netty - 眼熟NIO

Netty系列文章 - 眼熟Nio

什么是IO?什么是NIO?

  1. IO:
    • java.io中最为核心的概念是流stream),面向流编程,java中,一个流要么是输入流,要么是输出流
  2. NIO:
    • java.nio中拥有3个核心概念:selector. channel. buffer; java.nio中,面向块(block)或是缓冲区(buffer)编程的,
    • java中的原生8种基本数据类型都有各自对应的buffer类型,(除Boolean外),如IntBuffer,CharBuffer,ByteBuffer,LongBuffer,ShortBuffer
    • 所有数据的读写都是通过buffer来进行的,永远不会出现直接channel中直接写入,读取数据
    • stream不同的是,channel双向的,一个流只可能是InputStream或是OutputStreamchannel则是双向的,channel打开后可以进行读又可以进行写

3个核心概念

Buffer

Buffer属性以及相关操作.

属性说明
capacity 最大容量它永远不可能为负数,并且是不会变化的
position 位置下一个读或写的位置,它永远不可能为负数,并且不会大于limit
limit 限制它永远不可能为负数,并且不会大于capacity
mark 标记标记位置,用于记录某次读写的位置,可以通过reset()方法回到这里
public class Dome1 {
    public static void main(String[] args) {

        // 分配一块容量大小为8个长度的Buffer块
        IntBuffer buffer = IntBuffer.allocate(5);
        
        // 生成随机数存入buffer中
        for (int i = 0; i < buffer.capacity(); i++) {
            int nextInt = new Random().nextInt(20);
            buffer.put(nextInt);
        }

        /**
         * 
         * 反转一下
         * 其实核心也就是执行了这两行代码
         *         limit = position;
         *         position = 0;
         */
        buffer.flip();
        
        
        while (buffer.hasRemaining()) {
            System.out.print(buffer.get()+"\t"); //输出结果: 3	1	4	5	10	
        }
    }
}

Buffer初始化完成后图示

在这里插入图片描述

put或get一次数据``position`就会往右移动一次

在这里插入图片描述

调用buffer.flip();后limit = position; position = 0;
在这里插入图片描述

Channel

常见的Channel实现有

FileChannel:文件读写数据通道
SocketChannel:TCP读写网络数据通道
ServerSocketChannel:服务端网络数据读写通道,可以监听TCP连接。对每一个新进来的连接都会创建一个SocketChannel: 客户端网络数据读写通道.
DatagramChannel:UDP读写网络数据通道

通道表示打开到 IO 设备(例如:文件、套接字)的连接。若需要使用 NIO 系统,需要获取用于连接 IO 设备的通道以及用于容纳数据的缓冲区。然后操作缓冲区,对数据进行处理。
Channel相比IO中的Stream更加高效,可以异步双向传输,但是必须和buffer一起使用。

// 读一个文件内容
public class Dome2 {

    public static void main(String[] args) throws Exception {

        FileInputStream fileInputStream = new FileInputStream("dome2.txt");
        FileChannel channel = fileInputStream.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(1024);

        channel.read(byteBuffer);
        byteBuffer.flip();

        while (byteBuffer.remaining() > 0) {
            System.err.println((char) byteBuffer.get());
        }

        fileInputStream.close();
    }

}

输出结果

H
e
l
l
o

// 从一个文件读数据并写到另一个文件内!
public class Dome4 {
    public static void main(String[] args) throws Exception {
        FileOutputStream fileOutputStream = new FileOutputStream("dome4write.txt");
        FileInputStream fileInputStream = new FileInputStream("dome4read.txt");

        FileChannel channelRead = fileInputStream.getChannel();
        FileChannel channelWrite = fileOutputStream.getChannel();
        ByteBuffer byteBuffer = ByteBuffer.allocate(100);
        while (true) {
          	// 注意这个clear方法
            byteBuffer.clear();
            System.out.println(byteBuffer.position());
            int readNumber = channelRead.read(byteBuffer);
            System.out.println(readNumber);
            if (-1 == readNumber) {
                break;
            }
            byteBuffer.flip();
            channelWrite.write(byteBuffer);
        }
        fileOutputStream.close();
        fileInputStream.close();
    }
}

Selector(选择器)(多路复用器)

1. Selector的创建

通过调用Selector.open()方法创建一个Selector对象,如下:

Selector selector = Selector.open();

2. 注册Channel到Selector

channel.configureBlocking(false);
SelectionKey key = channel.register(selector, Selectionkey.OP_READ);

Channel必须是非阻塞的
所以FileChannel不适用Selector,因为FileChannel不能切换为非阻塞模式,更准确的来说是因为FileChannel没有继承SelectableChannel。Socket channel可以正常使用。

SelectableChannel抽象类 有一个 configureBlocking() 方法用于使通道处于阻塞模式或非阻塞模式。

abstract SelectableChannel configureBlocking(boolean block)  

示例代码

NIOServer端

public static void main(String[] args) throws IOException {

    //得到serverSocketChannel对象   老大
    ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

    //得到Selector对象   间谍
    Selector selector = Selector.open();

    //绑定端口
    serverSocketChannel.bind(new InetSocketAddress(9090));

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

    //把ServerSocketChannel注册给Selector
    serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);   //监听连接

    //干活
    while (true) {
        try {
            //监控客户端
            if (selector.select(2000) == 0) {
                System.out.println("2秒内没有客户端来连接我");
                continue;
            }
            //得到SelectionKey对象,判断是事件
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            for (SelectionKey selectionKey : selectionKeys) {
                if (selectionKey.isAcceptable()) {     //连接事件
                    System.out.println("有人来连接");
                    //获取网络通道
                    SocketChannel clientSocket = serverSocketChannel.accept();
                    //设置非阻塞式
                    clientSocket.configureBlocking(false);
                    //连接上了  注册读取事件
                    clientSocket.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }
                if (selectionKey.isReadable()) {     //读取客户端数据事件
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
                    socketChannel.read(byteBuffer);
                    System.out.println(new String(byteBuffer.array()));
                    byteBuffer.put("你好".getBytes());
                    byteBuffer.flip();
                    socketChannel.write(byteBuffer);
                }
                if (selectionKey.isWritable()) {
                    SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
                    ByteBuffer byteBuffer = (ByteBuffer) selectionKey.attachment();
                    byteBuffer.put("你好".getBytes());
                    byteBuffer.clear();
                    socketChannel.write(byteBuffer);
                    selectionKey.interestOps(SelectionKey.OP_READ);
                }
                //手动从当前集合将本次运行完的对象删除
                selectionKeys.remove(selectionKey);
            }
        } catch (Exception e) {
            System.out.println("错了");
        }
    }

}

NIOClient客户端

public static void main(String[] args) throws Exception {
    //得到一个网络通道
    SocketChannel socketChannel = SocketChannel.open();
    //设置非阻塞式
    socketChannel.configureBlocking(false);
    //提供服务器ip与端口
    InetSocketAddress inetSocketAddress = new InetSocketAddress("127.0.0.1", 9090);
    //连接服务器端
    if (!socketChannel.connect(inetSocketAddress)) {     //如果连接不上
        while (!socketChannel.finishConnect()) {
            System.out.println("nio非阻塞");
        }
    }
    new Thread(new MyRunble(socketChannel)).start();
    while (true) {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        int read = socketChannel.read(buffer);
        if (read > 0) {
            System.out.println(new String(buffer.array()));
        }
    }
}


static class MyRunble implements Runnable {
    SocketChannel socketChannel;

    MyRunble(SocketChannel channel) {
        this.socketChannel = channel;
    }

    @Override
    public void run() {
        while (true) {
            //创建一个buffer对象并存入数据
            Scanner scanner = new Scanner(System.in);
            String message = scanner.nextLine();
            ByteBuffer buffer = ByteBuffer.wrap(message.getBytes());
            //发送数据
            try {
                socketChannel.write(buffer);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页