前言:
netty学习的第一篇文章,讲讲内容是啥。最近在极客时间上学netty相关的课,老师上课方式很有问题,每节课先抛出几个问题,在之后的内容中解答这几个问题。而我的话,在看完课之后,再拿着问题,自己解答一遍,这样学习的效率很不错。所以接下来的几篇文章就都是我的学习总结了。第一篇是粘包、半包。
粘包:一个包里发了多个数据
原因:
1.写入的数据<缓冲区大小(缓冲区没满,就不发)
2.接收方从缓冲区读取的速度太慢
半包:一个包里只有半条数据
原因:
1。写入的数据>缓冲区大小
2.写入的数据大于协议的MTU(最大传输单元),必须拆包
解决粘包和半包的常用方法:
1.固定长度
2.设置分隔符
3.固定长度字段存内容的长度信息(和现在的协议里很像,先给个长度,然后根据长度取截取)
粘包、半包的解决方式:
1.解码核心工作流程
先说:
维护一个数据积累器,根据不同的解码方式,进行解码
* For example here is an implementation which reads all readable bytes from
* the input {@link ByteBuf} and create a new {@link ByteBuf}.
*
* <pre>
* public class SquareDecoder extends {@link ByteToMessageDecoder} {
* {@code @Override}
* public void decode({@link ChannelHandlerContext} ctx, {@link ByteBuf} in, List<Object> out)
* throws {@link Exception} {
* out.add(in.readBytes(in.readableBytes()));
* }
* }
* </pre>
通过创建一个decoder继承byteToMessageDecoder
final ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap
// 组装 NioEventLoopGroup
.group(bossGroup, workerGroup)
// 设置channel类型为 NIO 类型
.channel(NioServerSocketChannel.class)
// 设置连接配置参数 服务端可连接队列数,对应 TCP/IP 协议 listen 函数中 backlog 参数
.option(ChannelOption.SO_BACKLOG, 1024)
// 长连接
.childOption(ChannelOption.SO_KEEPALIVE, true)
// 立即写出
.childOption(ChannelOption.TCP_NODELAY, true)
// 配置入站、出站事件handler
.handler(new LoggingHandler(LogLevel.DEBUG))
.childHandler(gbt32960ChannelInitializer);
先创建serverBootstrap
然后加载childHandler
GBT32960ChannelInitializer extends ChannelInitializer<NioSocketChannel>
@Override
protected void initChannel(NioSocketChannel nioSocketChannel) {
ChannelPipeline pipeline = nioSocketChannel.pipeline();
// 心跳检测
pipeline.addLast("idleStateHandler",
new IdleStateHandler(readTimeOut, 0, 0, TimeUnit.MINUTES));
// 分隔符解码器配置 0x23 0x23
// pipeline.addLast(new DelimiterBasedFrameDecoder(
// 1100, Unpooled.copiedBuffer(new byte[]{DELIMITER_GBT32960_23, DELIMITER_GBT32960_23})));
pipeline.addLast(new GBT32960BaseDecoder());
pipeline.addLast(new GBT32960Decoder());
pipeline.addLast(new GBT32960Encoder());
pipeline.addLast(businessGroup, gbt32960ClientRealDataMsgHandler);
pipeline.addLast(businessGroup, gbt32960ClientLoginMsgHandler);
pipeline.addLast(businessGroup, gbt32960ClientLogoutMsgHandler);
pipeline.addLast(businessGroup, gbt32960DataPacketHandler);
}
pipeLine里面添加decoder,继承byteToMessageDecoder,重写其decode方法。
GBT32960BaseDecoder extends ByteToMessageDecoder {
如果要用默认的decoder的话,直接
pipeline.addLast(new FixedLengthFrameDecoder());
提供几个粘包半包的解决方式。上面有一堆提供的。(这里部分是目前项目中根据业务自己创建的decode方法)