Netty学习笔记(一)

一、预备知识

1.BIO

​ Java BIO(Blocking IO) 是阻塞IO,工作原理图如下:

在这里插入图片描述

​ 特点:

  • ​ 服务器需要为每一个客户端的连接请求启动一个线程,当并发数较大时,需要创建大量线程来处理连接,系统资源占用较大。
  • ​ 连接建立后,如果当前线程暂时没有数据可读,则线程就阻塞在 Read 操作上,造成线程资源浪费。

​ 如果连接少,他的延迟是最低的,因为一个线程只处理一个连接,适用于连接数不是很多,但是需要连续传输大量数据的场景。比如数据库连接:数据库的连接 都是事先创建好的 , 而且 数据库端和java端通常会有大量的数据传输。

2.NIO

​ NIO(Non-Blocking IO) 同步非阻塞IO,工作原理如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WlNGLQ4W-1619070441425)(/Users/lilun/Library/Application Support/typora-user-images/image-20210415111419171.png)]

​ Java NIO的非阻塞模式,使一个线程从某通道发送请求读取数据,但是它仅能得到目前可用的数据,如果目前没有数据可用时,就什么都不会获取,而不是保持线程阻塞,所以直至数据变的可以读取之前,该线程可以继续做其他的事情。 非阻塞写也是如此,一个线程请求写入一些数据到某通道,但不需要等待它完全写入,这个线程同时可以去做别的事情。

​ 特点:

  • ​ 高性能,吞吐量更高,延迟更低;减少资源消耗;
  • ​ 传输快,原有的 IO 是面向流的、阻塞的,NIO 则是面向块的、非阻塞的(buffer的优势)。

三大组件:channel,Buffer,selector

3.Channel(通道)

  • 既可以从通道中读取数据,又可以写数据到通道。但流的读写通常是单向的。
  • 通道可以异步地读写。
  • 通道中的数据总是要先读到一个Buffer,或者总是要从一个Buffer中写入。

在这里插入图片描述

这些是Java NIO中最重要的通道的实现:

  • FileChannel
  • DatagramChannel
  • SocketChannel
  • ServerSocketChannel

FileChannel 从文件中读写数据。

DatagramChannel 能通过UDP读写网络中的数据。

SocketChannel 能通过TCP读写网络中的数据。

ServerSocketChannel可以监听新进来的TCP连接,像Web服务器那样。对每一个新进来的连接都会创建一个SocketChannel。

4.Buffer(缓冲区)

​ 缓冲区本质上是一块可以写入数据,然后可以从中读取数据的内存。这块内存被包装成NIO Buffer对象,并提供了一组方法,用来方便的访问该块内存。

​ 基于 Buffer 操作不像传统 IO 的顺序操作,NIO 中可以随意地读取任意位置的数据。

​ 为了理解Buffer的工作原理,需要熟悉它的三个属性:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NPMm0TI8-1619070441429)(/Users/lilun/Library/Application Support/typora-user-images/image-20210415113042525.png)]

1) capacity

​ 作为一个内存块,Buffer有一个固定的大小值,也叫“capacity”.你只能往里写capacity个byte、long,char等类型。一旦Buffer满了,需要将其清空(通过读数据或者清除数据)才能继续写数据往里写数据。

2) position

​ 当你写数据到Buffer中时,position表示当前的位置。初始的position值为0.当一个byte、long等数据写到Buffer后, position会向前移动到下一个可插入数据的Buffer单元。position最大可为capacity – 1.

​ 当读取数据时,也是从某个特定位置读。当将Buffer从写模式切换到读模式,position会被重置为0. 当从Buffer的position处读取数据时,position向前移动到下一个可读的位置。

3) limit

​ 在写模式下,Buffer的limit表示你最多能往Buffer里写多少数据。 写模式下,limit等于Buffer的capacity。

​ 当切换Buffer到读模式时, limit表示你最多能读到多少数据。因此,当切换Buffer到读模式(调用filp方法)时,limit会被设置成写模式下的position值。换句话说,你能读到之前写入的所有数据(limit被设置成已写数据的数量,这个值在写模式下就是position)

在这里插入图片描述

5.Selector

​ Selector(选择器)是Java NIO中能够检测一到多个NIO通道,并能够知晓通道是否为诸如读写事件做好准备的组件。这样,一个单独的线程可以管理多个channel,从而管理多个网络连接。

6.简单的nio范例

/**
 * <p>Description:客户端</p>
 *
 */
public class NIOCilent {
    public static void main(String[] args) throws Exception{
        //得到网络通道
        SocketChannel socketChannel = SocketChannel.open();
        //非阻塞
        socketChannel.configureBlocking(false);
        InetSocketAddress localhost = new InetSocketAddress("127.0.0.1", 6666);
        if(!socketChannel.connect(localhost)){
            while(!socketChannel.finishConnect()){
                System.out.println("因为链接需要时间,客户端不会阻塞");
            }
        }
        String str ="hello";
        ByteBuffer buffer = ByteBuffer.wrap(str.getBytes());
        socketChannel.write(buffer);
        System.in.read();
    }
}

/**
 * <p>Description:服务端</p>
 *
 */
public class NIOServer {
    public static void main(String[] args) throws Exception {
        //创建serversocketchannel
        ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
        //得到selector对象
        Selector selector = Selector.open();
        //绑定端口
        serverSocketChannel.socket().bind(new InetSocketAddress(6666));
        //非阻塞
        serverSocketChannel.configureBlocking(false);
        //注册到selector
        serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

        while (true) {
            if (selector.select(1000) == 0) {
                System.out.println("服务器等待了1秒,无连接");
                continue;
            }
            //获得相关的集合
            //如何返回大于0,表示以获取到关注的时间
            //返回关住事件的集合
            //通过key反向获取通道
            Set<SelectionKey> selectionKeys = selector.selectedKeys();
            Iterator<SelectionKey> iterator = selectionKeys.iterator();
            while (iterator.hasNext()) {
                //获得key
                SelectionKey key = iterator.next();
                //根据key对应的通道发生的事件做处理
                //如果是OP_Accept,有新的链接
                if (key.isAcceptable()) {
                    //该客户端生成了一个socketChannel
                    SocketChannel socketChannel = serverSocketChannel.accept();
                    //将但钱的channel注册到selector上,关注事件为read,同时关联一个buffer
                    System.out.println("客户端链接成功,生成一个socketChannel" + socketChannel.hashCode());
                    socketChannel.configureBlocking(false);
                    socketChannel.register(selector, SelectionKey.OP_READ, ByteBuffer.allocate(1024));
                }

                if (key.isReadable()) {
                    //通过key反向获取对应的channel
                    SocketChannel channel = (SocketChannel)key.channel();
                    //获得关联的buffer
                    ByteBuffer buffer = (ByteBuffer)key.attachment();
                    channel.read(buffer);
                    System.out.println("from 客户端" + new String(buffer.array()));
                }
                iterator.remove();
            }
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值