物联网 基于netty理解粘包/拆包

物联网 基于netty理解粘包/拆包

简述

TCP 是流式协议,数据像水流一样到达。发送方连续发送多条消息时,接收方可能发生

复制代码
粘包:多条消息合并成一条收到
拆包:一条消息被拆成多次收到
例如客户端发送 "Hello" 和 "Netty",服务器可能收到 "HelloNetty" 或 "Hel"+"loNetty"。
仅用 StringDecoder 无法还原原始消息边界

源码

https://gitee.com/kcnf-iot/iot-sample/tree/master/netty/netty-sample-00

LengthFieldBasedFrameDecoder

  • 核心参数(本例使用)

    maxFrameLength:最大帧长度(防攻击)
    lengthFieldOffset:长度字段偏移量(0,从开头)
    lengthFieldLength:长度字段字节数(4,int 类型)
    lengthAdjustment:长度字段补偿值(0)
    initialBytesToStrip:剥离字节数(4,去掉长度字段,只保留数据)

粘包/拆包代码

server 端
复制代码
package com.jysemel.iot;

import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;
import java.nio.charset.StandardCharsets;

public class EchoServerWithFrameDecoder {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap bootstrap = new ServerBootstrap();
            bootstrap.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // ----- 关键点:解决粘包/拆包 -----
                            // 1. 长度字段解码器:最大帧长1024,长度字段从偏移0开始占4字节,不调整,剥离4字节长度字段
                            pipeline.addLast(new LengthFieldBasedFrameDecoder(1024, 0, 4, 0, 4));
                            // 2. 将帧内容(字节)转为字符串
                            pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            // 3. 出站编码(服务器向客户端写字符串时转为字节)
                            pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            // 4. 业务处理器
                            pipeline.addLast(new EchoServerHandler());
                        }
                    });
            ChannelFuture future = bootstrap.bind(8182).sync();
            System.out.println("EchoServerWithFrameDecoder 启动,端口 8182");
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}
client 端
复制代码
package com.jysemel.iot;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.LengthFieldPrepender;
import io.netty.handler.codec.string.StringDecoder;
import io.netty.handler.codec.string.StringEncoder;

import java.nio.charset.StandardCharsets;

public class EchoClientWithFrameDecoder {
    public static void main(String[] args) throws InterruptedException {
        EventLoopGroup group = new NioEventLoopGroup();
        try {
            Bootstrap bootstrap = new Bootstrap();
            bootstrap.group(group)
                    .channel(NioSocketChannel.class)
                    .handler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch) {
                            ChannelPipeline pipeline = ch.pipeline();
                            // ----- 关键点:发送消息时自动添加4字节长度前缀 -----
                            pipeline.addLast(new LengthFieldPrepender(4));
                            pipeline.addLast(new StringEncoder(StandardCharsets.UTF_8));
                            pipeline.addLast(new StringDecoder(StandardCharsets.UTF_8));
                            pipeline.addLast(new EchoClientHandler());
                        }
                    });
            ChannelFuture future = bootstrap.connect("127.0.0.1", 8182).sync();
            future.channel().closeFuture().sync();
        } finally {
            group.shutdownGracefully();
        }
    }
}

运行与验证

复制代码
服务端收到: 消息_1
服务端收到: 消息_2
服务端收到: 消息_3
服务端收到: 消息_4
服务端收到: 消息_5

总结

组件 作用
LengthFieldBasedFrameDecoder 入站解码器,根据长度字段从 TCP 流中截取出一个个完整的数据帧,解决粘包/拆包。
LengthFieldPrepender 出站编码器,在消息前添加长度字段(通常与解码器参数对应)。
StringDecoder 将帧内的字节数组转为字符串(需保证帧内是完整的 UTF-8 字节)。
相关推荐
YG亲测源码屋21 小时前
java配置环境变量、jdk环境变量配置、java环境变量设置方法
java·开发语言
MIUMIUKK21 小时前
从语法层面,看懂 Python 的特殊处
java·开发语言·python
hujinyuan201601 天前
2026年3月 中国电子学会青少年软件编程(Python)三级考试试卷 真题及答案
java·python·算法
basketball6161 天前
C++ 高级编程:2. 基本线程池实现
java·开发语言·c++
MageGojo1 天前
天气 API 接入实战:基于 ApiZero 实现实时天气、分钟级降水和 15 天预报查询
java·后端·spring·api 接口接入·接口实战
黎阳之光1 天前
数智赋能水厂全链路安全|黎阳之光以视频孪生技术落地供水精细化管控
人工智能·物联网·算法·安全·数字孪生
自动跟随1 天前
UWB自动跟随技术全栈解析:从定位算法到“位控一体化“
java·网络·人工智能
喜欢打篮球的普通人1 天前
LLVM 后端流程与关键数据结构:从 IR 到机器码的入门笔记
java·数据结构·笔记
弹简特1 天前
【Java项目-轻聊】07-实现主页面模块
java·开发语言
wuminyu1 天前
Java锁机制之轻量级锁判断与尝试逻辑源码剖析
java·linux·c语言·jvm·c++