1、Netty 之 不得不说的NIO技术

概念介绍

1、 BIO(blocking I/O):同步阻塞IO,也即是传统的I/O。

2、 NIO (non-blocking IO): 也即是New I/O,使用它可以提供非阻塞式的高伸缩性网络。

3、AIO 即 NIO2.0, 叫做异步不阻塞的 IO。 AIO 引入异步通道的概念, 采用了 Proactor 模式, 简化了程序编写,有效的请求才启动线程, 它的特点是先由操作系统完成后才通知服务端程序启动线程去处理, 一般适用于连接。

NIO的三大神器

        NIO弥补了原来的同步阻塞I/O的不足,它在标准java代码中提供了高速的 、面向块的I/O。通过定义包含数据的类,以及通过以块的形式处理这些数据,NIO不适用本机代码就可以利用低级优化,  这是与BIO的本质区别。NIO主要包含三大组件应用:Buffer缓冲区Channel通道多路复用器Selector

       Buffer缓冲区

        Buffer就是一个对象,它包含一些要写入的或者要读出的数据。NIO中加入了Buffer对象,这也是体现了与传统IO的一个重要区别。在NIO中,所有的数据都是用Buffer缓冲区来处理的。在读取数据时,它是直接读到缓冲区中;再写入数据时,也是写入到缓冲区中。任何时候访问NIO数据,都是通过缓冲区进行操作。

        Buffer缓冲区实质上是一个数组,通常它是一个ByteBuffer(字节数组),当然也可以使用其他类的数组。但是一个缓冲区不仅仅是一个数组,缓冲区提供了对数据结构化访问以及维护读写位置(limit)等信息。最常用的ByteBuffer,一个ByteBuffer 提供了一组功能操作byte数组。

        ByteBuffer:字节缓冲区   

        CharBuffer:字符缓冲区       

        ShortBuffer:短整型缓冲区

        IntBuffer:整形缓冲区

        LongBuffer:长整型缓冲区

        FloatBuffer:浮点型缓冲区

        DoubleBuffer:双精度浮点型缓冲区

继承关系图:

        Channel通道

        Channel通道,它类似一个自来水管一样的模型,网络数据通过Channel读取和写入。通道与流的区别是通道是双向的。流只是一个方向的移动。而Channel可以用于读、写或者二者同时进行。

Channel主要分为两大类:用于网络读写的SelectableChannel 和用于文件操作的FileChannel。

而我们使用的ServerSocketChannel SocketChannel 都是 SelectableChannel的子类。

         多路复用器Selector

        多路复用器提供了选择已经就绪的任务的能力。简单的说,Selector会不断的轮询注册​​​注册在其上的Channel,如果某个Channel上边发生了读或者写的事件,这个Channel就处于就绪状态,会被Selector轮询出来。然后通过SelectionKey可以获取就绪的Channel集合,进行I/O操作。一个多路复用器可以同时轮询多个Channel,JDK使用了epoll()代替了原来的select的实现。这就意味着只需要一个线程就可以接入成千上万的客户端。

        NIO服务端通信序列图:如下

         详解NIO客户端主要的创建过程:

1、打开ServerSocketChannel,用于监听客户端链接,它是所有客户端链接的父管道。

ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();

2、绑定需要监听的接口,设置链接模式为非阻塞模式

serverSocketChannel.socket().bind(new InetSocketAddress(InetAddress.getByName("IP"),port));
serverSocketChannel.configureBlocking(false);

3、创建Reactor线程,创建多路复用器并启动线程。

Selector selector = Selector.open();
new Thread(new ReactorTask()).start();

4、将ServerSocketChannel 注册到复用selector上

SelectionKey key = serverSocketChannel.register(selector,SelectionKey.OP_ACCEPT,ioHandler);

5、多路复用器在线程run方法的无限循环体内轮询准备就绪的Key

int num = selector.select();
Set selectionKeys = selector.selectedKeys();
Iterator it = selectedKeys.iterator();
while(it.hasNext()){
    SelectionKey key = (SelectionKey)it.next();
    // 处理I/O 的事件(Event)
}

6、多路复用器听到有新的客户端接入,处理新的请求,完成TCP三次握手,建立物理链路。

SocketChannel socketChannel = serverSocketChannel.accept();

7、设置客户端链路为非阻塞模式

socketChannel.configureBlocking(false);
socketChannel.socket().setReuseAddress(true);

8、将新接入的客户连接到Reactor线程的多路复用器上,监听读写操作,读取客户端发送的网络信息。

SelectionKey key = socketChannel.register(selector,SelectionKey.OP_ACCEPT,ioHandler);

9、异步读取客户端请求消息到缓冲区

int readNum = socketChannel.read(receivedBuffer);

10、对ByteBuffer进行编解码(接收数据处理、写出数据的预处理)操作。

11、调用SocketChannel 的异步接口write 接口进行消息的写入,发送给客户端。

简例:

package com.example.nio;

import com.sun.org.apache.bcel.internal.generic.Select;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.channels.SelectableChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.ServerSocketChannel;
import java.util.Iterator;
import java.util.Set;

public class Demo1 {
    public static void main(String[] args) {

    }

    class Server implements Runnable {
        private Selector selector;
        private ServerSocketChannel serverSocketChannel;
        private volatile boolean stop;


        public Server(int port) throws IOException {
            selector = Selector.open();
            serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);
            serverSocketChannel.socket().bind(new InetSocketAddress(port), 1024);
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);
        }

        public void stop() {
            this.stop = true;
        }

        @Override
        public void run() {
            while (!stop){
                try {
                    selector.select(1000);//1秒的等待
                    Set<SelectionKey> selectionKeys = selector.selectedKeys();
                    Iterator iterator = selectionKeys.iterator();
                    SelectionKey key = null;
                    while (iterator.hasNext()){
                        key= (SelectionKey) iterator.next();
                        iterator.remove();
                        handleInput(key);
                        if(key!=null){
                            key.cancel();
                            if(key.channel()!=null){
                                key.channel().close();
                            }
                        }
                        
                    }
                    
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }

        }
        
        public void handleInput(SelectionKey key){
            //判定事件
            //进行编解码
            //处理业务
        }
    }
}

NIO就写到这里了~~~~,后续想到遗漏的会进行补充的,谢谢~~

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

艾利克斯冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值