Bootstrap, ServerBootstrap
- Bootstrap 意思是引导, 一个Netty应用通常由一个Bootstrap开始, 主要作用的配置整个Netty程序, 串联各个组件, Netty中Bootstrap类是客户端程序的启动引导类, ServerBootstrap是服务器端程序的启动引导类
- 常见的方法有:
- public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup), 用于设置服务端的BossGroup和WorkerGroup
- public B group(EventLoopGroup group), 用于设置客户单的EventLoopGroup
- public B channel(Class<? extends C> channelClass), 该方法用来设置一个服务器端的通道实现
- public <T> B option(ChannelOption<T> option, T value), 该方法用来ServerChannel添加配置
- public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value), 用来给接收到的通道添加配置
- public ServerBootstrap childHandler(ChannelHandler childHandler), 该方法用来设置业务处理类(自定义的Handler)
- public ChannelFuture bind(int inetPort), 该方法用于服务端,用来设置绑定的端口
- public ChannelFuture connect(InetAddress inetHost, int inetPort), 该方法用于客户端, 用来连接服务器端
Future, ChannelFuture
Netty中所有的IO操作都是异步的, 不能立刻得知消息是否被正确处理, 但是可以过一会等它执行完成或者直接注册一个监听器, 具体的实现就是通过Future和ChannelFuture, 他们可以注册一个监听, 当操作执行成功或者失败时监听会自动触发注册的监听事件
常见方法:
- Channel channel() , 返回当前正在进行 IO 的通道
- ChannelFuture sync() , 等待异步操作执行完成
Channel
- Netty网络通信的组件,能够用于执行网络IO操作
- 通过channel可获得当前网络连接的通道的状态
- 通过channel可获得网络连接的配置参数(例如接收缓冲区大小)
- channel提供异步的网络IO操作(如建立连接, 读写,绑定端口), 异步调用意味着任何IO调用都将立即返回,并且不保证在调用结束时所请求的IO操作已完成
- 调用立即返回一个ChannelFuture实例, 通过注册监听器到ChannelFuture上, 可以IO操作成功, 失败或取消时回调通知方
- 支持关联IO操作与对应的处理程序
- 不同协议, 不同的阻塞类型的连接都有不同的Channel类型与之对应,常用的Channel类型:
- NioSocketChannel, 异步的客户端 TCP socket连接
- NioServerSocketChannel, 异步的服务器端TCP socket连接
- NioDatagramChannel, 异步的UDP连接
- NioSctpChannel, 异步的客户端Sctp连接
- NioSctpServerChannel, 异步的Sctp服务器端连接
- 这些通道涵盖了UDP和TCP网络IO以及文件IO
Selector
- Netty基于Selector对象实现IO多路复用, 通过Selector一个线程可以监听多个连接的Channel事件
- 当一个Selector中注册Channel后, Selector内部的机制就可以自动不断的查询(Select)这些注册的Channel是否有已就绪的IO事件(例如可读, 可写, 网络连接完成等), 这样程序就可以很简单的使用一个线程高效的管理多个Channel
ChannelHandler 及其实现类
- ChannelHandler是一个接口, 处理IO事件或者拦截IO操作, 并将其转发到其ChannelPipeline(业务处理链)中的下一个处理程序
- ChannelHandler本身并没有提供很多方法, 因为这个接口有很多的方法需要去实现, 方便使用期间, 可以集成他的子类
- ChannelHandler及其实现类一览图(后)
- 我们经常需要自定义一个handler类去继承ChannelInboundHandlerAdapter, 然后重写相应的方法实现业务逻辑, 我们接下来看看一般都需要重写那些方法
Pipeline 和 ChannelPipeline
ChannelPipeline是一个重点:
- ChannelPipeline是一个Handler的集合, 它负责处理和拦截inbound和outbound的事件和操作, 相当于一个贯穿Netty 的链. (也可以这样理解, ChannelPipeline是保存ChannelHandler的List, 用于处理或拦截Channel的入站事件和出站操作)
- ChannelPipeline实现了一种高效形式的拦截过滤器模式, 使用户可以完全控制事件的处理方式, 以及Channel 中各个的ChannelHandler如何交互
- 在Netty中每个Channel都有且仅有一个ChannelPipeline与之对应, 他们的组成关系如下
- 常用方法
- ChannelPipeline addFirst(ChannelHandler... handlers), 把一个业务处理类(Handler), 添加到链中的第一个位置
- ChannelPipeline addLast(ChannelHandler... handlers), 把一个业务处理类(Handler), 添加到链中的最后一个位置
入站 出站
ChannelHandlerContext
- 保存Channel相关信息的上下文对象, 同时关联一个CHannel对象
- 即ChannelHandlerContext中包含一个具体的事件处理器ChannelHandler, 同时ChannelHandlerContext 中也绑定了对应的pipeline和Channel信息,方便对ChannelHandler进行调用
- 常用方法:
ChannelOption
- Netty在创建Channel实例后, 一般都需要设置ChannelOption参数
- ChannelOption参数如下:
EventLoopGroup和其实现类NioEventLoopGroup
- EventLoopGroup是一组EventLoop的抽象,Netty 为了更好的利用多核CPU资源, 一般会有多个EventLoop同时工作, 每个EventLoop维护者一个Selector实例
- EventLoopGroup提供next接口, 可以从组里面按照一定规则获取其中一个EventLoop来处理任务, 在Netty服务器端编程中, 我们一般都需要提供两个EventLoopGroup 例如:BossEventLoopGroup 和 WorkerEventLoopGroup
- 通常一个服务器端口即一个ServerSocketChannel 对应一个 Selector 和一个EventLoop线程, BOSSEventLoop负责接收客户端的连接并将SocketChannel交给WorkerEventLoopGroup来进行IO处理,如下图所示
- 常用方法
- public NioEventLoopGroup() 构造方法
- public Future<?> shutdownGracefully(), 断开连接关闭线程
Unpooled类
- Netty提供了一个专门用来操作缓冲区(即 Netty的数据容器)的工具类
- 常用方法如下
- 举例说明Unpooled获取Netty的数据容器ByteBuf的基本使用[案例演示]
案例一
package com.dance.netty.netty.buf; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; public class NettyByteBuf01 { public static void main(String[] args) { /* * 创建一个ByteBuf * 创建对象, 该对象包含一个数组arr, 是一个byte[10] * 在Netty的Buffer中, 不需要使用flip进行反转 * 底层维护了readerIndex 和 writerIndex * 通过readerIndex, writerIndex, capacity 将buffer分为三个区域 * 0 -> readerIndex : 已读区域 * readerIndex -> writerIndex : 未读区域 * writerIndex -> capacity : 可写区域 */ ByteBuf buffer = Unpooled.buffer(10); for (int i = 0; i < 10; i++) { buffer.writeByte(i); } System.out.println("capacity is "+buffer.capacity()); // readByte会使 readerIndex增长 如果指定位置则不会 for (int i = 0; i < buffer.capacity(); i++) { System.out.println(buffer.readByte()); } } }
案例二
package com.dance.netty.netty.buf; import io.netty.buffer.ByteBuf; import io.netty.buffer.Unpooled; import java.nio.charset.StandardCharsets; public class NettyByteBuf02 { public static void main(String[] args) { ByteBuf byteBuf = Unpooled.copiedBuffer("hello,world!", StandardCharsets.UTF_8); // 调用相关的方法 if(byteBuf.hasArray()){ // 获取字节数组 byte[] array = byteBuf.array(); // 转为字符串输出 System.out.println(new String(array,StandardCharsets.UTF_8)); // 输出ByteBuf System.out.println("byte buf is " + byteBuf); // 数组偏移量 System.out.println(byteBuf.arrayOffset()); // readerIndex 位置 System.out.println(byteBuf.readerIndex()); // writerIndex 位置 System.out.println(byteBuf.writerIndex()); // capacity 边界 System.out.println(byteBuf.capacity()); // 读取宇哥字节 System.out.println(byteBuf.readByte()); // 读取指定位置的一个字节 System.out.println(byteBuf.getByte(0)); // 可读的字节数 System.out.println(byteBuf.readableBytes()); for (int i = 0; i < byteBuf.readableBytes(); i++) { System.out.println((char)byteBuf.getByte(i)); } // 从0开始读取4个字节 System.out.println(byteBuf.getCharSequence(0, 4, StandardCharsets.UTF_8)); // 从4开始读取6个字节 System.out.println(byteBuf.getCharSequence(4, 6, StandardCharsets.UTF_8)); } } }