Netty
欢迎关注 小海博客
文章目录
1.什么是Netty
Netty是NIO客户端服务器框架,支持协议服务器和客户端等网络应用的快速简单开发。它极大地简化了网络编程,如TCP和UDP套接字服务器。Netty 是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如 FTP、SMTP、HTTP、许多二进制和基于文本的传统协议。因此,Netty成功地找到了一种方法,在不失灵活性的前提下来实现开发、性能、稳定性和灵活性
2.为什么是Netty
The problem
今天,我们使用通用的应用程序或者类库来实现互相通讯,比如,我们经常使用一个 HTTP 客户端库来从 web 服务器上获取信息,或者通过 web 服务来执行一个远程的调用。
然而,有时候一个通用的协议或他的实现并没有很好的满足需求。比如我们无法使用一个通用的 HTTP 服务器来处理大文件、电子邮件以及近实时消息,比如金融信息和多人游戏数据。我们需要一个高度优化的协议来处理一些特殊的场景。例如你可能想实现一个优化了的 Ajax 的聊天应用、媒体流传输或者是大文件传输器,你甚至可以自己设计和实现一个全新的协议来准确地实现你的需求。
另一个不可避免的情况是当你不得不处理遗留的专有协议来确保与旧系统的互操作性。在这种情况下,重要的是我们如何才能快速实现协议而不牺牲应用的稳定性和性能。
The Solution
Netty 是一个提供 asynchronous event-driven (异步事件驱动)的网络应用框架,是一个用以快速开发高性能、可扩展协议的服务器和客户端。
换句话说,Netty 是一个 NIO 客户端服务器框架,使用它可以快速简单地开发网络应用程序,比如服务器和客户端的协议。Netty 大大简化了网络程序的开发过程比如 TCP 和 UDP 的 socket 服务的开发。
“快速和简单”并不意味着应用程序会有难维护和性能低的问题,Netty 是一个精心设计的框架,它从许多协议的实现中吸收了很多的经验比如 FTP、SMTP、HTTP、许多二进制和基于文本的传统协议. 因此,Netty成功地找到了一种方法,在不失灵活性的前提下来实现开发、性能、稳定性和灵活性
3.Netty组件
Bootstrap
Netty 应用程序通过设置 bootstrap(引导)类的开始,该类提供了一个 用于应用程序网络层配置的容器。
Bootstrap有两种类型,一种是用于客户端的Bootstrap,一种是用于服务端的ServerBootstrap。不管程序使用哪种协议,无论是创建一个客户端还是服务器都需要使用“引导”。
Channel
底层网络传输 API 必须提供给应用 I/O操作的接口,如读,写,连接,绑定等等。对于我们来说,这是结构几乎总是会成为一个“socket”。 Netty 中的接口 Channel 定义了与 socket 丰富交互的操作集:bind, close, config, connect, isActive, isOpen, isWritable, read, write 等等。 Netty 提供大量的 Channel 实现来专门使用。这些包括 AbstractChannel,AbstractNioByteChannel,AbstractNioChannel,EmbeddedChannel, LocalServerChannel,NioSocketChannel 等等。
ChannelHandler
ChannelHandler 支持很多协议,并且提供用于数据处理的容器。我们已经知道 ChannelHandler 由特定事件触发。 ChannelHandler 可专用于几乎所有的动作,包括将一个对象转为字节(或相反),执行过程中抛出的异常处理。
常用的一个接口是 ChannelInboundHandler,这个类型接收到入站事件(包括接收到的数据)可以处理应用程序逻辑。当你需要提供响应时,你也可以从 ChannelInboundHandler 冲刷数据。一句话,业务逻辑经常存活于一个或者多个 ChannelInboundHandler。
ChannelPipeline
ChannelPipeline 提供了一个容器给 ChannelHandler 链并提供了一个API 用于管理沿着链入站和出站事件的流动。每个 Channel 都有自己的ChannelPipeline,当 Channel 创建时自动创建的。 ChannelHandler 是如何安装在 ChannelPipeline? 主要是实现了ChannelHandler 的抽象 ChannelInitializer。ChannelInitializer子类 通过 ServerBootstrap 进行注册。当它的方法 initChannel() 被调用时,这个对象将安装自定义的 ChannelHandler 集到 pipeline。当这个操作完成时,ChannelInitializer 子类则 从 ChannelPipeline 自动删除自身。
EventLoop
EventLoop 用于处理 Channel 的 I/O 操作。一个单一的 EventLoop通常会处理多个 Channel 事件。一个 EventLoopGroup 可以含有多于一个的 EventLoop 和 提供了一种迭代用于检索清单中的下一个。
ChannelFuture
Netty 所有的 I/O 操作都是异步。因为一个操作可能无法立即返回,我们需要有一种方法在以后确定它的结果。出于这个目的,Netty 提供了接口 ChannelFuture,它的 addListener 方法注册了一个 ChannelFutureListener ,当操作完成时,可以被通知(不管成功与否)。
方法解析
Bootstrap
AbstractBootstrap类
AbstractBootstrap是Bootstrap的基类, 类定义如下
public abstract class AbstractBootstrap*<*B extends AbstractBootstrap*<*B, C*>*, C extends Channel*>* implements Cloneable
其中泛型要求B为其本类的子类,C为channel的子类。
// 设置EventLoopGroup 在ServerBootstrap中调用此方法为设置parentGroup
public B group(EventLoopGroup group) {
// 空值检查
ObjectUtil.checkNotNull(group, "group");
if (this.group != null) {
throw new IllegalStateException("group set already");
}
this.group = group;
// self() 返回强转后的当前对象
return self();
}
// 设置Channel
public B channel(Class<? extends C> channelClass) {
// 通过ChannelFactory来获取Channel的实例对象
return channelFactory(new ReflectiveChannelFactory<C>(
ObjectUtil.checkNotNull(channelClass, "channelClass")
));
}
// 设置ChannelFactory
public B channelFactory(io.netty.channel.ChannelFactory<? extends C> channelFactory) {
return channelFactory((ChannelFactory<C>) channelFactory);
}
// 设置localAddress
public B localAddress(SocketAddress localAddress) {
this.localAddress = localAddress;
return self();
}
public B localAddress(int inetPort) {
return localAddress(new InetSocketAddress(inetPort));
}
public B localAddress(String inetHost, int inetPort) {
return localAddress(SocketUtils.socketAddress(inetHost, inetPort));
}
public B localAddress(InetAddress inetHost, int inetPort) {
return localAddress(new InetSocketAddress(inetHost, inetPort));
}
// 设置option的值 或者移除option
public <T> B option(ChannelOption<T> option, T value) {
ObjectUtil.checkNotNull(option, "option");
if (value == null) {
synchronized (options) {
options.remove(option);
}
} else {
synchronized (options) {
options.put(option, value);
}
}
return self();
}
// 设置key的值或者移除key
public <T> B attr(AttributeKey<T> key, T value) {
ObjectUtil.checkNotNull(key, "key");
if (value == null) {
synchronized (attrs) {
attrs.remove(key);
}
} else {
synchronized (attrs) {
attrs.put(key, value);
}
}
return self();
}
// 数据验证
public B validate() {
if (group == null) {
throw new IllegalStateException("group not set");
}
if (channelFactory == null) {
throw new IllegalStateException("channel or channelFactory not set");
}
return self();
}
// 创建新的channel并注册到EventLoop上
public ChannelFuture register() {
validate();
return initAndRegister();
}
//新建channnel并绑定它
public ChannelFuture bind() {
// 数据验证
validate();
SocketAddress localAddress = this.localAddress;
if (localAddress == null) {
throw new IllegalStateException("localAddress not set");
}
// 绑定
return doBind(localAddress);
}
public ChannelFuture bind(int inetPort) {
return bind(new InetSocketAddress(inetPort));
}
public ChannelFuture bind(String inetHost, int inetPort) {
return bind(SocketUtils.socketAddress(inetHost, inetPort));
}
public ChannelFuture bind(InetAddress inetHost, int inetPort) {
return bind(new InetSocketAddress(inetHost, inetPort));
}
public ChannelFuture bind(SocketAddress localAddress) {
validate();
return doBind(ObjectUtil.checkNotNull(localAddress, "localAddress"));
}
// 设置handler
public B handler(ChannelHandler handler) {
this.handler = ObjectUtil.checkNotNull(handler, "handler");
return self();
}
// 获取配置
public abstract AbstractBootstrapConfig<B, C> config();
ServerBootstrap类
用于服务端简单的引导ServerChannel。
public ServerBootstrap() { } // 无参构造器
// 单参数group方法 设置parentGroup和childGroup 都为group
public ServerBootstrap group(EventLoopGroup group) {
return group(group, group);
}
// 设置parentGroup和childGroup
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
super.group(parentGroup);
// 空值检查
ObjectUtil.checkNotNull(childGroup, "childGroup");
// 判断是否已经设置过childGroup
if (this.childGroup != null) {
throw new IllegalStateException("childGroup set already");
}
this.childGroup = childGroup;
return this;
}
// 设置ChannelOption
public <T> ServerBootstrap childOption(ChannelOption<T> childOption, T value) {
// 空值检查
ObjectUtil.checkNotNull(childOption, "childOption");
if (value == null) {
// 若为null则移除childOption
synchronized (childOptions) {
childOptions.remove(childOption);
}
} else {
// 设置childOption
synchronized (childOptions) {
childOptions.put(childOption, value);
}
}
return this;
}
// 设置AttributeKey
public <T> ServerBootstrap childAttr(AttributeKey<T> childKey, T value) {
// 空值检查
ObjectUtil.checkNotNull(childKey, "childKey");
if (value == null) {
// 若为null则移除childKey
childAttrs.remove(childKey);
} else {
// 设置childKey
childAttrs.put(childKey, value);
}
return this;
}
// 设置ChannelHandler
public ServerBootstrap childHandler(ChannelHandler childHandler) {
// 判空并赋值
this.childHandler = ObjectUtil.checkNotNull(childHandler, "childHandler");
return this;
}
// 验证所有的参数
public ServerBootstrap validate() {
// 父类中有验证group(前面group方法设置的parentGroup) 和 channelFactory(用以创建Channel实例的工厂的实例对象)
super.validate();
// 验证childHandler (ChannelHandler实例对象)
if (childHandler == null) {
throw new IllegalStateException("childHandler not set");
}
//验证childGroup (group方法设置的 EventLoopGroup childGroup)
if (childGroup == null) {
// 未设置childGroup 默认使用parentGroup
logger.warn("childGroup is not set. Using parentGroup instead.");
childGroup = config.group();
}
return this;
}
// 返回一个克隆后的实例对象
public ServerBootstrap clone() {
return new ServerBootstrap(this);
}
// 返回配置信息
public final ServerBootstrapConfig config() {
return config;
}
ServerBootstrapConfig类
ServerBootstrap的配置类。ServerBootstrapConfig继承自AbstractBootstrapConfig,是一个配置类,可以通过各个方法获取对于的配置值
Bootstrap类
public Bootstrap() { } // 无参构造方法
// 默认设置为DefaultAddressResolverGroup.INSTANCE, 可以通过传入非null值来赋值:
public Bootstrap resolver(AddressResolverGroup<?> resolver) {
DEFAULT_RESOLVER : resolver);
return this;
}
// 设置连接的远程地址和端口
public Bootstrap remoteAddress(SocketAddress remoteAddress) {
this.remoteAddress = remoteAddress;
return this;
}
public Bootstrap remoteAddress(String inetHost, int inetPort) {
remoteAddress = InetSocketAddress.createUnresolved(inetHost, inetPort);
return this;
}
public Bootstrap remoteAddress(InetAddress inetHost, int inetPort) {
remoteAddress = new InetSocketAddress(inetHost, inetPort);
return this;
}
//进行连接
public ChannelFuture connect() {
// 参数验证
validate();
// 地址判空
SocketAddress remoteAddress = this.remoteAddress;
if (remoteAddress == null) {
throw new IllegalStateException("remoteAddress not set");
}
// 连接
return doResolveAndConnect(remoteAddress, config.localAddress());
}
/** 省略多个connect重载函数 参数均为不同形式的设置远程连接的地址和端口 */
// 参数验证
public Bootstrap validate() {
//父类参数验证
super.validate();
handler非空验证
if (config.handler() == null) {
throw new IllegalStateException("handler not set");
}
return this;
}
// 返回此类实例的克隆实例
public Bootstrap clone() {
return new Bootstrap(this);
}
public Bootstrap clone(EventLoopGroup group) {
Bootstrap bs = new Bootstrap(this);
bs.group = group;
return bs;
}
// 返回配置信息
public final BootstrapConfig config() {
return config;
}
BootstrapConfig类
Bootstrap的配置类也继承自AbstractBootstrapConfig,类方法也几乎与ServerBootstrap相同,具体方法如下图
EventLoop
NioEventLoopGroup类
多个构造函数以及设置子事件循环中I/O所需时间的百分比的setIoRatio方法和一个rebuildSelectors方法
EventLoopGroup类
// 获取下一个EventLoop
@Override
EventLoop next();
//注册channel
ChannelFuture register(Channel channel);
// 使用ChannelFuture注册一个channel
ChannelFuture register(ChannelPromise promise);
Channel
Channel接口
//获取基础属性
ChannelId id()
Channel parent()
ChannelConfig config()
ChannelMetadata metadata(): 获取TCP参数配置
//获取运行时属性
EventLoop eventLoop()
Channel.Unsafe unsafe()
ChannelPipeline pipeline()
ByteBufAllocator alloc()
//获取状态
boolean isOpen()
boolean isRegistered()
boolean isActive()
boolean isWritable()
//获取和网络相关属性
SocketAddress localAddress()
SocketAddress remoteAddress()
//获取各种Future和Promise
ChannelFuture closeFuture()
ChannelPromise newPromise()
ChannelProgressivePromise newProgressivePromise()
ChannelFuture newSucceededFuture()
ChannelFuture newFailedFuture(Throwable cause)
ChannelPromise voidPromise()
//IO操作
ChannelFuture bind(SocketAddress localAddress)
ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise)
ChannelFuture connect(SocketAddress remoteAddress)
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress)
ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise)
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise)
ChannelFuture disconnect()
ChannelFuture disconnect(ChannelPromise promise)
ChannelFuture close()
ChannelFuture close(ChannelPromise promise)
ChannelFuture deregister()
ChannelFuture deregister(ChannelPromise promise)
Channel read()
ChannelFuture write(Object msg)
ChannelFuture write(Object msg, ChannelPromise promise)
Channel flush()
ChannelFuture writeAndFlush(Object msg)
ChannelFuture writeAndFlush(Object msg, ChannelPromise promise)
Channel Handler
ChannelHandler接口
// 在ChannelHandler被添加到实际的context并准备处理事件后被调用
void handlerAdded(ChannelHandlerContext ctx) throws Exception;
// 在ChannelHandler被从实际的context中删除并不再处理事件后被调用
void handlerRemoved(ChannelHandlerContext ctx) throws Exception;
//标注一个channel handler可以被多个channel安全地共享。
@Inherited
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@interface Sharable {
// no value
}
ChannelInboundHandlerAdapter类
ChannelInboundHandlerAdapter是常规情况下用户实现ChannelHandler的继承类,其与ChannelHandler接口的关系如下ChannelInboundHandlerAdapter中主要实现了ChannelInboundHandler接口。
ChannelInboundHandler接口
channelRegistered(); ChannelHandlerContext的Channel被注册到EventLoop
channelUnregistered(); ChannelHandlerContext的Channel被从EventLoop中注销
channelActive(); ChannelHandlerContext的Channel被激活
channelInactive(); 被注册的ChannelHandlerContext的Channel现在被取消并到达了它生命周期的终点
channelRead(); 当Channel读取到一个消息时被调用
channelReadComplete(); 当前读操作读取的最后一个消息被channelRead()方法消费时调用. 如果ChannelOption.AUTO_READ 属性被设置为off, 不会再尝试从当前channel中读取inbound数据, 直到ChannelHandlerContext.read()方法被调用.
userEventTriggered(); 当用户事件被触发时调用
channelWritabilityChanged(); 当channel的可写状态变化时被调用, 可以通过Channel.isWritable()来检查状态.
exceptionCaught(); 当Throwable被抛出时被调用
ChannelPipeline接口
ChannelPipeline用来管理ChannelHandler
// 增加ChannelHandler
/* addFirst()方法: 将handler添加到pipeline的第一个位置
* 参数中, name是当前要插入的handler的名字, 如果设置为null则自动生成.
* 注意: name不容许重复, 如果添加的时候发现已经存在同样name的handler, 则会抛出IllegalArgumentException.
*/
ChannelPipeline addFirst(String name, ChannelHandler handler);
ChannelPipeline addFirst(EventExecutorGroup group, String name, ChannelHandler handler);
ChannelPipeline addFirst(ChannelHandlerInvoker invoker, String name, ChannelHandler handler);
/* addLast()方法
* 和addFirst()方法完全对应
* addBefore()方法
* 参数中, name和addFirst()中一致, 但是多了一个baseName, 这个是插入的基准handler的名字, 即要插在这个名字的handle前面.
*/
ChannelPipeline addBefore(String baseName, String name, ChannelHandler handler);
ChannelPipeline addBefore(EventExecutorGroup group, String baseName, String name, ChannelHandler handler);
ChannelPipeline addBefore(ChannelHandlerInvoker invoker, String baseName, String name, ChannelHandler handler);
/**
* addAfter()方法
* 和addBefore()方法完全对应.
* 另外以上方法还有可以参数的方法重载, 无非就是将参数中的一个handler变成ChannelHandler... handlers.
*/
// 删除ChannelHandler
/**
* remove()方法查找给定参数的handler, 并从ChannelPipeline中删除它, 然后将被删除的handle返回:
* 如果删除时发现找不到要删除的目标, 这些remove()方法会抛出NoSuchElementException, 这个要小心处理.
*/
ChannelPipeline remove(ChannelHandler handler);
ChannelHandler remove(String name);
<T extends ChannelHandler> T remove(Class<T> handlerType);
ChannelHandler removeLast();
ChannelHandler removeFirst();
// 替换ChannelHandler
/**
*replace()方法用于替换原来的handler为新的handler,
* 如果替换时发现找不到要替换的目标, replace()方法会抛出NoSuchElementException.
*/
ChannelPipeline replace(ChannelHandler oldHandler, String newName, ChannelHandler newHandler);
ChannelHandler replace(String oldName, String newName, ChannelHandler newHandler);
<T extends ChannelHandler> T replace(Class<T> oldHandlerType, String newName, ChannelHandler newHandler);
// 获取ChannelHandler
//如果pipeline为空, 则上面的方法返回null值.
ChannelHandler first();
ChannelHandler last();
ChannelHandler get(String name);
<T extends ChannelHandler> T get(Class<T> handlerType);
// 获取ChannelHandlerContext
//如果pipeline为空或者pipeline中找不到要求的hanlder, 则上面的方法返回null值.
ChannelHandlerContext firstContext();
ChannelHandlerContext lastContext();
ChannelHandlerContext context(ChannelHandler handler);
ChannelHandlerContext context(String name);
ChannelHandlerContext context(Class< ? extends ChannelHandler> handlerType);
// 其他管理handler的方法
List<String> names();
Map<String, ChannelHandler> toMap();
//获取Channel
Channel channel();
// 如果当前pipeline 还没有绑定到channel, 则你这里返回null.
//fire方法
//fire开头的这些方法是给事件通知用的:
// channel被注册到eventloop
ChannelPipeline fireChannelRegistered();
// channel被从eventloop注销
ChannelPipeline fireChannelUnregistered();
// channel被激活, 通常是连接上了
ChannelPipeline fireChannelActive();
// channle被闲置, 通常是被关闭
ChannelPipeline fireChannelInactive();
// channel收到一个Throwable, 比较有意思的是javadoc中明确指出发生的地点是"in one of its inbound operations"
ChannelPipeline fireExceptionCaught(Throwable cause);
// channel收到一个用户自定义事件
ChannelPipeline fireUserEventTriggered(Object event);
//还有几个方法是给channel读写的:
// channel收到信息
ChannelPipeline fireChannelRead(Object msg);
ChannelPipeline fireChannelReadComplete();
ChannelPipeline fireChannelWritabilityChanged();
//I/O操作方法
ChannelFuture bind(SocketAddress localAddress);
ChannelFuture connect(SocketAddress remoteAddress);
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress);
ChannelFuture disconnect();
ChannelFuture close();
ChannelFuture deregister();
//以及带ChannelPromise的变体版本:
ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise);
ChannelFuture connect(SocketAddress remoteAddress, ChannelPromise promise);
ChannelFuture connect(SocketAddress remoteAddress, SocketAddress localAddress, ChannelPromise promise);
ChannelFuture disconnect(ChannelPromise promise);
ChannelFuture close(ChannelPromise promise);
ChannelFuture deregister(ChannelPromise promise);
//I/O读写方法
ChannelPipeline read();
ChannelFuture write(Object msg);
ChannelFuture write(Object msg, ChannelPromise promise);
ChannelPipeline flush();
ChannelFuture writeAndFlush(Object msg, ChannelPromise promise);
ChannelFuture writeAndFlush(Object msg);
Buffer
ByteBuf 有 2 部分:一个用于读,一个用于写。我们可以按顺序的读取数据,并且可以跳到开始重新读一遍。 所有的数据操作, 我们只需要做的是调整读取数据索引和再次开始读操作。 写入数据到 ByteBuf 后, 写入索引是增加的字节数量。 开始读字节后, 读取索引增加。
使用 Netty 时会遇到 3 种不同类型的 ByteBuf:
- Heap Buffer( 堆缓冲区)
最常用的类型是 ByteBuf 将数据存储在 JVM 的堆空间,这是通过将数据存储在数组的实现。堆缓冲区可以快速分配,当不使用时也可以快速释放。它还提供了直接访问数组的方法,通过ByteBuf.array()来获取 byte[]数据。 访问非堆缓冲区 ByteBuf 的数组会导致 UnsupportedOperationException, 可以使用ByteBuf.hasArray()来检查是否支持访问数组。
- Direct Buffer( 直接缓冲区)
直接缓冲区,在堆之外直接分配内存。直接缓冲区不会占用堆空间容量,使用时应该考虑到应用程序要使用的最大内存容量以及如何限制它。直接缓冲区在使用 Socket 传递数据时性能很好,因为若使用间接缓冲区,JVM 会先将数据复制到直接缓冲区再进行传递;但是直接缓冲区的缺点是在分配内存空间和释放内存时比堆缓冲区更复杂, 而 Netty使用内存池来解决这样的问题,这也是 Netty 使用内存池的原因之一。直接缓冲区不支持数组访问数据,但是我们可以间接的访问数据数组
- Composite Buffer(复合缓冲区)
复合缓冲区,我们可以创建多个不同的 ByteBuf,然后提供一个这些 ByteBuf
组合的视图。复合缓冲区就像一个列表,我们可以动态的添加和删除其中的 ByteBuf,JDK 的ByteBuffer 没有这样的功能。Netty 提供了 CompositeByteBuf 类来处理复合缓冲区,CompositeByteBuf只是一个视图,CompositeByteBuf.hasArray()总是返回 false,因为它 可能包含一些直接或间接的不同类型的 ByteBuf。
常用方法:
- 读操作 :get/read get不会改变读索引,read会改变读索引
- 写操作 set/write
- 索引管理
- 查找
- 副本
- 其他
Codec
codec的作用就是将原始字节数据与目标程序数据格式进行互转。网络中都是以字节码的数据形式来传输数据的,codec 由两部分组成:decoder(解码器)和encoder(编码器)
Decoder(解码器)
ByteToMessageDecoder类
ByteToMessageDecoder 是用于将字节转为消息(或其他字节序列)。
你不能确定远端是否会一次发送完一个完整的“信息”,因此这个类会缓存入站的数据,直到准备好了用于处理
类方法如下
public void setSingleDecode(boolean singleDecode)// 设置singleDecode
public boolean isSingleDecode() //如果为true,则每个channelRead()调用仅解码一条消息。因为会影响性能,故默认值为false
public void setCumulator(Cumulator cumulator) // 设置ByteToMessageDecoder.Cumulator用于累积接收到的ByteBuf。
public void setDiscardAfterReads(int discardAfterReads) // 设置调用discardsomereadbytes()的读取次数,以便释放内存
ReplayingDecoder类
ReplayingDecoder 是 byte-to-message 解码的一种特殊的抽象基类,读取缓冲区的数据之前需要检查缓冲区是否有足够的字节,使用ReplayingDecoder就无需自己检查;若ByteBuf中有足够的字节,则会正常读取;若没有足够的字节则会停止解码。
类方法:
MessageToMessageDecoder类
用于从一种消息解码为另外一种消息
Encoder(编码器)
MessageToByteEncoder类
将消息编码为字节
欢迎关注 小海博客