深入理解Netty编解码、粘包拆包、心跳机制( 六 )
测试结果正确:
文章插图
分析Protocol的粘包、拆包实际上直接使用Protocol编解码器还是存在粘包问题的 。
证明一下 , 发送端循环一百次发送100条"一角钱 , 起飞"的消息 , 请看发送端代码演示:
@Overridepublic void channelActive(ChannelHandlerContext ctx) throws Exception { for (int i = 1; i <= 100; i++) {MessagePojo.Message message = MessagePojo.Message.newBuilder().setId(i).setContent(i + "号一角钱 , 起飞~").build();ctx.writeAndFlush(message); }}
这时 , 启动服务端 , 客户端后 , 可能只有打印几条消息或者在控制台看到如下错误:
com.google.protobuf.InvalidProtocolBufferException: While parsing a protocol message, the input ended unexpectedly in the middle of a field. This could mean either that the input has been truncated or that an embedded message misreported its own length.
意思是:分析protocol消息时 , 输入意外地在字段中间结束 。 这可能意味着输入被截断 , 或者嵌入的消息误报了自己的长度 。
其实就是粘包问题 , 多条数据合并成一条数据了 , 导致解析出现异常 。
解决Protocol的粘包、拆包问题只需要在发送端加上编码器 ProtobufVarint32LengthFieldPrepender
@Overrideprotected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ProtobufVarint32LengthFieldPrepender());ch.pipeline().addLast(new ProtobufEncoder());ch.pipeline().addLast(new TcpClientHandler());}
接收方加上解码器 ProtobufVarint32FrameDecoder
@Overrideprotected void initChannel(SocketChannel ch) throws Exception { ch.pipeline().addLast(new ProtobufVarint32FrameDecoder()); ch.pipeline().addLast(new ProtobufDecoder(MessagePojo.Message.getDefaultInstance())); //给pipeline管道设置处理器 ch.pipeline().addLast(new TcpServerHandler());}
然后再启动服务端和客户端 , 我们可以看到正常了~
ProtobufVarint32LengthFieldPrepender 编码器的工作如下:
* BEFORE ENCODE (300 bytes)AFTER ENCODE (302 bytes) * +---------------++--------+---------------+ * | Protobuf Data |-------------->| Length | Protobuf Data | * |(300 bytes)|| 0xAC02 |(300 bytes)| * +---------------++--------+---------------+@Sharablepublic class ProtobufVarint32LengthFieldPrepender extends MessageToByteEncoder {@Overrideprotected void encode(ChannelHandlerContext ctx, ByteBuf msg, ByteBuf out) throws Exception {int bodyLen = msg.readableBytes();int headerLen = computeRawVarint32Size(bodyLen);//写入请求头 , 消息长度out.ensureWritable(headerLen + bodyLen);writeRawVarint32(out, bodyLen);//写入数据out.writeBytes(msg, msg.readerIndex(), bodyLen);}}
ProtobufVarint32FrameDecoder 解码器的工作如下:
* BEFORE DECODE (302 bytes)AFTER DECODE (300 bytes) * +--------+---------------++---------------+ * | Length | Protobuf Data |----->| Protobuf Data | * | 0xAC02 |(300 bytes)||(300 bytes)| * +--------+---------------++---------------+public class ProtobufVarint32FrameDecoder extends ByteToMessageDecoder {@Overrideprotected void decode(ChannelHandlerContext ctx, ByteBuf in, List
- 全新8核国产CPU深入探秘:马上能买到
- 数据|新基建时代,高大全的数据管理解决方案是怎样“炼”成的?
- 16G运存+256G内存,专业骁龙865旗舰,性价比深入人心
- 绿色骑行深入校园,共享单车长途长时需求量提升
- 不被理解的超时代发明,你知道几个?在线膜拜大神,西瓜视频真相
- 深入调查SolarWinds黑客事件 微软已查封一个核心服务器
- 《深入理解Java虚拟机》:Java内存区域
- 基于Netty高性能RPC框架Nifty协议、传输层、编解码
- 深入探讨 JavaScript 逻辑赋值运算符
- 彻底理解 IO 多路复用实现机制