类
ChannelInboundHandler<br>ChannelOutBoundHandler
inBoundHandler 的执行顺序与我们实际的添加顺序相同,而 outBoundHandler 则相反。<br>
ChannelInboundHandlerAdapter <br>
@Override<br>public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {<br><b><font color="#c41230"> ctx.fireChannelRead(msg);<br></font></b>}
接收上一个 handler 的输出,这里的 msg 就是上一个 handler 的输出,<font color="#c41230">msg 对象其实就是 ByteBuf</font>
默认情况下 adapter 会通过 fireChannelRead() 方法直接把上一个 handler 的输出结果传递到下一个 handler
ChannelOutboundHandlerAdapter
@Override<br>public void write(ChannelHandlerContext ctx, Object msg, ChannelPromise promise) throws Exception {<br> ctx.write(msg, promise);<br>}
这个 adapter 也会把对象传递到下一个 outBound 节点,它的传播顺序与 inboundHandler 相反
特殊的 handler
ByteToMessageDecoder
无论我们是在客户端还是服务端,当我们收到数据之后,首先要做的事情就是把二进制数据转换到我们的一个 Java 对象,所以 Netty 很贴心地写了一个父类,来专门做这个事情
自动管理ByteBuf申请的堆外存
SimpleChannelInboundHandler
public class LoginRequestHandler extends SimpleChannelInboundHandler<LoginRequestPacket> {<br> @Override<br> protected void channelRead0(ChannelHandlerContext ctx, LoginRequestPacket loginRequestPacket) {<br> // 业务逻辑<br> }<br>}
类型的转换都自动处理完成,不需要手动编码
MessageToByteEncoder
基于 MessageToByteEncoder,我们可以实现自定义编码,而不用关心 ByteBuf 的创建,不用每次向对端写 Java 对象都进行一次编码
生命周期
handlerAdded() :指的是当检测到新连接之后,调用 ch.pipeline().addLast(new LifeCyCleTestHandler()); 之后的回调,表示在当前的 channel 中,已经成功添加了一个 handler 处理器。
channelRegistered():这个回调方法,表示当前的 channel 的所有的逻辑处理已经和某个 NIO 线程建立了绑定关系
channelActive():当 channel 的所有的业务逻辑链准备完毕(也就是说 channel 的 pipeline 中已经添加完所有的 handler)以及绑定好一个 NIO 线程之后,这条连接算是真正激活了,接下来就会回调到此方法
channelRead():客户端向服务端发来数据,每次都会回调此方法,表示有数据可读
channelReadComplete():服务端每次读完一次完整的数据之后,回调该方法,表示数据读取完毕
拆包粘包理论与解决方案
为什么会有这样子的现象
TCP 底层和Netty应用层 不对等的关系
拆包原理<br>
如果当前读取的数据不足以拼接成一个完整的业务数据包,那就保留该数据,继续从 TCP 缓冲区中读取,直到得到一个完整的数据包
如果当前读到的数据加上已经读取的数据足够拼接成一个数据包,那就将已经读取的数据拼接上本次读取的数据,构成一个完整的业务数据包传递到业务逻辑,多余的数据仍然保留,以便和下次读到的数据尝试拼接。<br>
自带拆包器
固定长度的拆包器 FixedLengthFrameDecoder
行拆包器 LineBasedFrameDecoder
分隔符拆包器 DelimiterBasedFrameDecoder
基于长度域拆包器 LengthFieldBasedFrameDecoder
参考https://www.jianshu.com/p/a0a51fd79f62