Netty原理详解系列(一)---NIO中的Buffer&Chanel

1.概述

在BIO API中是通过InputStream 与outPutStream 两个流进行输入输出。而NIO 使用一个双向通信的管道代替了它俩。管道(Channel)必须依赖缓冲区(Buffer)实现通信。

管道对比流多了一些如:非阻塞、堆外内存映射、零拷贝等特性。

2.缓冲区Buffer定义

管道要依赖于缓冲区,所以先来介绍一下缓冲区的概念和使用。

缓冲区内部维护了一个数组来存储数据,缓存区并不支持存储的任意的数据类型,只能存储一些基本数据类型。

具备读写、清空的功能。不具备线程安全,需要自行的控制线程安全问题。

3.Buffer内部结构

拿ByteBuffer举例,它是Buffer的一个子类,用于存储Byte类型的数据,其他的缓冲区差不多,只是存储的数据类型不同。

内部维护了一个byte数组,用来存储数据的。

父类Buffer有4个重要的属性:

  • capacity

    表示数组的容量大小

  • limit

    限制Buffer的可读和可写的范围,默认等于capacity

  • position

    当前读写的位置,默认是0,每读取一位或写一位,则+1.

  • mark

    做标记,用于reset将position设置到这个位置。默认是-1

4者的位置关系:mark <= position <= limit <= capacity

4.Buffer常用的操作

4.1 allocate

分配缓冲区的存储空间,初始化所有的属性。例如:

在这里插入图片描述
得到的结果如下:
在这里插入图片描述

4.2 wrap

基于数组包装一个Buffer,position为0,limit为容量值

测试代码如下:

在这里插入图片描述
buffer结果如下:

在这里插入图片描述

4.3 put

写入缓冲区,测试代码如下:

在这里插入图片描述

在put的过程中,会改变position,也就是说每写入一个数字,就会将position+1.当第7次写入的时候,position会超过limit的限制,此时会抛出异常BufferOverflowExeception
在这里插入图片描述

4.4 flip

为读取做好准备,在put完之后,执行flip可以重置position,设置成0.

之后的读取操作就从position的位置开始。

测试代码如下:

在这里插入图片描述
执行完put 5 之后 buffer的结果如下:

在这里插入图片描述
接着执行flip,结果如下:
在这里插入图片描述
根据结果就可以看出flip的作用,将position设置成0

并且将limit设置成了原先position的位置,此时的limit就限制了读的范围。

4.5 get

读取缓冲区的内容,每读取一个,position位置后移动一位

测试代码如下:

在这里插入图片描述

当读取到第5个的时候,此时的buffer里面的内容如下:
在这里插入图片描述
可以发现此时的position已经到了limit的限制边界。如果再次读取就会报错,读取越界异常BufferUnderflowException
在这里插入图片描述

4.6 mark

设置标记位,记录下当前的位置

测试代码如下:

在这里插入图片描述

执行完mark后,将mark属性设置成position,也就是做了个标记
在这里插入图片描述

4.7 reset

reset的作用就是将position的值设置成mark,用于修改缓冲区中的一段数据。或者重复的读取缓冲区中的一段数据。

测试代码如下:
在这里插入图片描述

执行完reset之后,buffer结果如下

在这里插入图片描述

在记录mark标记后为,读取了两个数字,并将者两个数字修改了。reset之后,position又回到了mark记录的位置。此时再进行写操作,就可以修改缓冲区中的那两个数字。结果如下

在这里插入图片描述

4.8 clear

重置缓冲区的属性,但是并不会清空缓冲区里的数据

将position的位置设为0,mark设置为-1.limit设置为capacity

测试代码如下:

在这里插入图片描述

执行完clear之后,缓冲区的结果如下:

在这里插入图片描述

4.9 rewind

为重新读取做准备。将position设置为0,limit不变。mark设置为-1

测试代码如下:
在这里插入图片描述
执行完rewind之后,buffer结果如下:
在这里插入图片描述

4.10 remaining

返回还有多少可读取的范围

也就是limit-position

测试代码如下:
在这里插入图片描述
此时limit等于5 读取了两次之后,执行remaining方法,返回的结果是3,表示剩余可读取的内容

5. chanel

管道用于连接文件、网络Socket等。它可同时执行读取和写入两个I/O 操作,固称双向管道,它有连接和关闭两个状态,在创建管道时处于打开状态,一但关闭 在调用I/O操作就会报ClosedChannelException 。通过管道的isOpen 方法可判断其是否处于打开状态。

5.1 FileChannel 文件管道

顾名思义它就是用于操作文件的,除常规操作外它还支持以下特性:

  • 支持对文件的指定区域进行读写

  • 堆外内存映射,进行大文件读写时,可直接映射到JVM声明内存之外,从面提升读写效率。

  • 零拷贝技术,通过 transferFromtransferTo 直接将数据传输到某个通道,极大提高性能。

  • 锁定文件指定区域,以阻止其它程序员进行访问

打开FileChannel目前只能通过流进行间打开,如inputStream.getChannel() 和outputStream.getChannel() ,通过输入流打开的管道只能进行取,而outputStream打开的只能写。否则会分别抛出NonWritableChannelException与NonReadableChannelException异常。

如果想要管道同时支持读写,必须用RandomAccessFile 读写模式才可以。

下面的测试代码是文件管道的基本使用

				//1. 打开文件管道
        FileChannel channel = new RandomAccessFile(file_name,"rw").getChannel();

        ByteBuffer buffer=ByteBuffer.allocate(1024); // 声明1024个空间
        // 从文件中 读取数据并写入管道 再写入缓冲
        channel.read(buffer);
        buffer.flip();//上面学的,写完之后,需要将position归0,为了后面的读取做准备
        byte[] bytes= new byte[buffer.remaining()];
        int i =0;
        while (buffer.hasRemaining()){
            bytes[i++]= buffer.get();
        }
        System.out.println(new String(bytes));

        // 把缓冲区数据写入到管道
        channel.write(ByteBuffer.wrap("森林大帅哥".getBytes()));
        channel.close();
5.2 DatagramChannel UDP套接字管道

UDP是无连接的协议,DatagramChannel就是为这个协议提供服务,以接收客户端发来的消息。

DatagramChannel的基本用法如下:

public void test1() throws IOException {
    DatagramChannel channel=DatagramChannel.open();
    // 绑定端口
    channel.bind(new InetSocketAddress(8080));
    ByteBuffer buffer=ByteBuffer.allocate(8192);

    while (true){
        buffer.clear(); //  清空还原
        channel.receive(buffer); // 阻塞
        buffer.flip();
        byte[] bytes=new byte[buffer.remaining()];
        buffer.get(bytes);
        System.out.println(new String(bytes));
    }
}

使用命令nc - uv 127.0.0.1 8080 可以向指定的ip端口号 发送udp

在这里插入图片描述
idea控制台就会输出如下:

在这里插入图片描述

5.3 TCP套接字管道

TCP是一个有连接协议,须建立连接后才能通信。这就需要下面两个管道:

  • **ServerSocketChannel :**用于与客户端建立连接

  • **SocketChannel :**用于和客户端进行消息读写

测试代码如下:

@Test
public void test1() throws IOException {
  	// 用于与客户端建立连接
    ServerSocketChannel channel = ServerSocketChannel.open();
    channel.bind(new InetSocketAddress(8080));
    while (true) {//循环接收请求,分配子线程去执行请求
      	// 用于和客户端进行消息读写
        SocketChannel socketChannel = channel.accept();
        handle(socketChannel);
    }
}


public void handle(final SocketChannel socketChannel) throws IOException {
    // 2.通信
    Thread thread = new Thread(new Runnable() {
        @Override
        public void run() {
            ByteBuffer buffer = ByteBuffer.allocate(8192);
            while (true) {
                try {
                    buffer.clear();
                    socketChannel.read(buffer);
                    // 从buffer 当中读出来
                    buffer.flip();
                    byte[] bytes = new byte[buffer.remaining()];
                    buffer.get(bytes);
                    String message = new String(bytes);
                    System.out.println(message);
                    // 写回去
                    buffer.rewind();
                    socketChannel.write(buffer);
                    if (message.trim().equals("exit")) {
                        break;
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            try {
                socketChannel.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    });
    thread.start();
}

可通过命令进行测试TCP服务 telnet 127.0.0.1 8080

在这里插入图片描述
在这里插入图片描述

控制台输出了tcp的消息,并将消息写回了管道,命令行界面也输出了返回来的hello结果。

6.后续

下一篇博客,介绍NIO中的另一个重要组件Selector

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
### 回答1: Netty-WebSocket-Spring-Boot-Starter是一个用于将Websocket集成到Spring Boot应用程序的库。它使用Netty作为底层框架,提供了一种快速和可靠的方式来处理异步通信。 这个库提供了一种简单的方法来创建Websocket端点,只需要使用注释和POJO类即可。在这些端点上可以添加动态的事件处理程序,以处理连接、断开连接和消息事件等。 此外,Netty-WebSocket-Spring-Boot-Starter还包括了一些安全性的特性,如基于令牌的授权和XSS保护,可以帮助您保持您的Websocket应用程序安全。 总的来说,Netty-WebSocket-Spring-Boot-Starter提供了一种快速和易于使用的方式来构建Websocket应用程序,使得它成为应用程序开发人员的有用工具。 ### 回答2: netty-websocket-spring-boot-starter 是一个开源的 Java Web 开发工具包,主要基于 Netty 框架实现了 WebSocket 协议的支持,同时集成了 Spring Boot 框架,使得开发者可以更加方便地搭建 WebSocket 服务器。 该工具包提供了 WebSocketServer 配置类,通过在 Spring Boot 的启动配置类调用 WebSocketServer 配置类,即可启动 WebSocket 服务器。同时,该工具包还提供了多种配置参数,如端口号、URI 路径、SSL 配置、认证配置等等,可以根据业务需求进行自定义配置。 此外,该工具包还提供了一些可扩展的接口和抽象类,如 WebSocketHandler、ChannelHandlerAdapter 等,可以通过继承和实现这些接口和抽象类来实现业务逻辑的处理和拓展。 总的来说,netty-websocket-spring-boot-starter 提供了一个高效、简单、易用的 WebSocket 服务器开发框架,可以减少开发者的开发成本和工作量。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值