Socket粘包问题终极解决方案—Netty版(2W字)!(9)

简介: Socket粘包问题终极解决方案—Netty版(2W字)!(9)

2.封装消息解决粘包问题


此解决方案的核心是将消息分为消息头 + 消息体,在消息头中保存消息体的长度,从而确定一条消息的边界,这样就避免了粘包和半包问题了,它的实现过程如下图所示:


微信图片_20220120155406.jpg


在 Netty 中可以通过 LengthFieldPrepender(编码)和 LengthFieldBasedFrameDecoder(解码)两个类实现消息的封装。和上一个解决方案类似,我们需要分别在服务器端和客户端通过设置通道(Channel)来解决粘包问题。

服务器端的核心代码如下:


/**
 * 服务端通道初始化
 */
static class ServerInitializer extends ChannelInitializer<SocketChannel> {
    // 字符串编码器和解码器
    private static final StringDecoder DECODER = new StringDecoder();
    private static final StringEncoder ENCODER = new StringEncoder();
    // 服务器端连接之后的执行器(自定义的类)
    private static final NettyExample.ServerHandler SERVER_HANDLER = new NettyExample.ServerHandler();
    /**
     * 初始化通道的具体执行方法
     */
    @Override
    public void initChannel(SocketChannel ch) {
        // 通道 Channel 设置
        ChannelPipeline pipeline = ch.pipeline();
        // 18 行:消息解码:读取消息头和消息体
        pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4));
        // 20 行:消息编码:将消息封装为消息头和消息体,在消息前添加消息体的长度
        pipeline.addLast(new LengthFieldPrepender(4));
        // 设置(字符串)编码器和解码器
        pipeline.addLast(DECODER);
        pipeline.addLast(ENCODER);
        // 服务器端连接之后的执行器,接收到消息之后的业务处理
        pipeline.addLast(SERVER_HANDLER);
    }
}


其中核心代码是 18 行和 20 行,通过 LengthFieldPrepender 实现编码(将消息打包成消息头 + 消息体),通过 LengthFieldBasedFrameDecoder 实现解码(从封装的消息中取出消息的内容)。


LengthFieldBasedFrameDecoder 的参数说明如下:


  • 参数 1:maxFrameLength - 发送的数据包最大长度;


  • 参数 2:lengthFieldOffset - 长度域偏移量,指的是长度域位于整个数据包字节数组中的下标;


  • 参数 3:lengthFieldLength - 长度域自己的字节数长度;


  • 参数 4:lengthAdjustment – 长度域的偏移量矫正。如果长度域的值,除了包含有效数据域的长度外,还包含了其他域(如长度域自身)长度,那么,就需要进行矫正。矫正的值为:包长 - 长度域的值 – 长度域偏移 – 长度域长;


  • 参数 5:initialBytesToStrip – 丢弃的起始字节数。丢弃处于有效数据前面的字节数量。比如前面有 4 个节点的长度域,则它的值为 4。


LengthFieldBasedFrameDecoder(1024,0,4,0,4) 的意思是:数据包最大长度为 1024,长度域占首部的四个字节,在读数据的时候去掉首部四个字节(即长度域)。


客户端的核心实现代码如下:


/**
 * 客户端通道初始化类
 */
static class ClientInitializer extends ChannelInitializer<SocketChannel> {
    // 字符串编码器和解码器
    private static final StringDecoder DECODER = new StringDecoder();
    private static final StringEncoder ENCODER = new StringEncoder();
    // 客户端连接成功之后业务处理
    private static final NettyExample.ClientHandler CLIENT_HANDLER = new NettyExample.ClientHandler();
    /**
     * 初始化客户端通道
     */
    @Override
    public void initChannel(SocketChannel ch) {
        ChannelPipeline pipeline = ch.pipeline();
        // 消息解码:读取消息头和消息体
        pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4));
        // 消息编码:将消息封装为消息头和消息体,在响应字节数据前面添加消息体长度
        pipeline.addLast(new LengthFieldPrepender(4));
        // 设置(字符串)编码器和解码器
        pipeline.addLast(DECODER);
        pipeline.addLast(ENCODER);
        // 客户端连接成功之后的业务处理
        pipeline.addLast(CLIENT_HANDLER);
    }
}
相关文章
|
3月前
|
网络协议
【Netty 网络通信】Socket 通信原理
【1月更文挑战第9天】【Netty 网络通信】Socket 通信原理
|
4月前
|
编解码 缓存 移动开发
TCP粘包/拆包与Netty解决方案
TCP粘包/拆包与Netty解决方案
45 0
|
7月前
|
移动开发 网络协议 算法
由浅入深Netty粘包与半包解决方案
由浅入深Netty粘包与半包解决方案
50 0
|
13天前
|
网络协议 Java 物联网
Spring Boot与Netty打造TCP服务端(解决粘包问题)
Spring Boot与Netty打造TCP服务端(解决粘包问题)
26 1
|
3月前
|
编解码
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战_自定义长度分包编解码码器
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战_自定义长度分包编解码码器
46 0
|
3月前
|
网络协议
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战
Netty Review - 优化Netty通信:如何应对粘包和拆包挑战
50 0
|
4月前
|
存储 网络协议 算法
Netty使用篇:半包&粘包
Netty使用篇:半包&粘包
|
5月前
|
监控
在Netty底层监控消息发送到Socket的时间
在Netty底层监控消息发送到Socket的时间
38 0
|
8月前
|
网络协议 网络安全
python-- socket 粘包、实现 ssh
python-- socket 粘包、实现 ssh
|
8月前
|
Nacos
Netty自定义消息协议的实现逻辑处理粘包拆包、心跳机制
Netty自定义消息协议的实现逻辑处理粘包拆包、心跳机制
106 0