千家信息网

TCP粘包拆包的概念以及Netty解决TCP粘包拆包实例

发表于:2026-11-03 作者:千家信息网编辑
千家信息网最后更新 2026年11月03日,这篇文章主要讲解了"TCP粘包拆包的概念以及Netty解决TCP粘包拆包实例",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"TCP粘包拆包的概念以及Ne
千家信息网最后更新 2026年11月03日TCP粘包拆包的概念以及Netty解决TCP粘包拆包实例

这篇文章主要讲解了"TCP粘包拆包的概念以及Netty解决TCP粘包拆包实例",文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习"TCP粘包拆包的概念以及Netty解决TCP粘包拆包实例"吧!

1、TCP粘包拆包

操作系统

我们都知道,操作系统的核心是内核,独立于普通的应用程序之外,可以访问受保护的内存空间,也有访问底层硬件设备的所有权限。
为了保护用户进程不能直接访问操作内核,保证内核的安全,操作系统划分为两部分,一部分为内核空间,另一部分为用户空间。

I/O模型

为了操作系统的安全考虑,进程是无法直接访问I/O设备的,必须通过系统调用请求内核来完成I/O操作,而内核会为每个I/O设备维护一个缓冲区(buffer)

整个请求过程为:

1、用户进程发起请求,内核接受到请求之后,从I/O设备获取数据到buffer中
2、再将buffer中的数据copy到用户进程的地址空间
3、该用户进程获取到数据之后再响应客户端

粘包和拆包

同样的,操作系统通过TCP协议发送数据的时候,也会先将数据存放在缓冲区中,假设缓冲区的大小为1024个字节

粘包

如果发送的数据包比较小,远小于缓冲区的大小,TCP会将多个数据包合并为一个数据包发送,这就发生了粘包

拆包

如果发送的数据包比较大,远大于缓冲区的大小,TCP会将数据包拆分为多个数据包发送,这就发生了拆包

  • 服务端分两次读到了两个独立的数据包,分别是D1和D2,没有粘包和拆包

  • 服务端一次接收到了两个数据包,D1和D2粘在一起,发生了粘包

  • 服务端分两次读到了两个数据包,一次读到了完整的D1包和D2的部分包D2_1,第二次读到了D2剩下的包D2_2

  • 服务端分两次读到了两个数据包,一次读到了D1的部分分D1_1,第二次读到了D1的剩下的包D1_2和完整的D2包

2、TCP粘包拆包解决

对于TCP粘包拆包问题,有以下4种解决办法

(1) 消息定长,例如每个数据包的大小都为128字节,如果不够,空位补空格

(2) 客户端在每个包的末尾使用固定的分隔符,例如\r\n,如果一个包被拆分了,则等待下一个包发送过来之后找到其中的\r\n,然后对其拆分后的头部部分与前一个包的剩余部分进行合并,这样就得到了一个完整的包

(3) 将消息分为头部和消息体,在头部中保存有当前整个消息的长度,只有在读取到足够长度的消息之后才算是读到了一个完整的消息

(4) 通过自定义协议进行粘包和拆包的处理

3、Netty粘包拆包示例

客户端向服务端发送3条消息,服务端在收到消息之后也回复客户端3条消息

客户端

public class HelloWorldClient {    private int port;    private String address;    public HelloWorldClient(int port, String address) {        this.port = port;        this.address = address;    }    public void start() {        EventLoopGroup group = new NioEventLoopGroup();        Bootstrap bootstrap = new Bootstrap();        bootstrap.group(group).channel(NioSocketChannel.class)                .handler(new ChannelInitializer(){                    @Override                    protected void initChannel(SocketChannel socketChannel) throws Exception {                        ChannelPipeline pipeline = socketChannel.pipeline();                                                pipeline.addLast("decoder", new StringDecoder());// 字符串解码和编码                        pipeline.addLast("encoder", new StringEncoder());                        pipeline.addLast("handler", new ChannelInboundHandlerAdapter(){                                                        @Override                            public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {                                System.out.println("客户端收到消息:[">

服务端

public class HelloWorldServer {    private int port;        public HelloWorldServer(int port) {        this.port = port;    }        public void start(){        EventLoopGroup bossGroup = new NioEventLoopGroup();//创建父子线程组        EventLoopGroup workGroup = new NioEventLoopGroup();                ServerBootstrap server = new ServerBootstrap();        server.group(bossGroup, workGroup)              .channel(NioServerSocketChannel.class)//指定处理客户端的通道              .childHandler(new ChannelInitializer(){                @Override                protected void initChannel(SocketChannel socketChannel) throws Exception {                    ChannelPipeline pipeline = socketChannel.pipeline();                                        pipeline.addLast("decoder", new StringDecoder());// 字符串解码和编码                    pipeline.addLast("encoder", new StringEncoder());                    pipeline.addLast("handler", new ChannelInboundHandlerAdapter(){                                                @Override                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {                            System.out.println("服务端收到消息:[" + msg + "]");                            ctx.writeAndFlush(Unpooled.copiedBuffer("我是服务端".getBytes()));                            ctx.writeAndFlush(Unpooled.copiedBuffer("我是服务端".getBytes()));                            ctx.writeAndFlush(Unpooled.copiedBuffer("我是服务端".getBytes()));                        }                    });//自定义handler                }              });//通道初始化        try {            ChannelFuture future = server.bind(port).sync();            future.channel().closeFuture().sync();        } catch (InterruptedException e) {            e.printStackTrace();        } finally{            bossGroup.shutdownGracefully();            workGroup.shutdownGracefully();        }    }    public static void main(String[] args) {        HelloWorldServer server = new HelloWorldServer(8888);        server.start();    }}

运行结果:
服务端

客户端

可以看到,客户端收到的3条消息都粘在一起了

4、Netty解决粘包拆包

4.1、采用定长解决

客户端添加FixedLengthFrameDecoder消息定长解码器,服务端不用处理

运行结果

可以看到,客户端收到的消息确实是按字节数来分割的,但是由于一条消息的长度超过10个字节,所以在遇到中文字符时,会发生乱码,这也是定长分隔符的不足之处

4.2、采用分隔符

客户端修改initChannel方法,自定义 $ 为分隔符

服务端在消息的末尾加上自定义分隔符 $

那么客户端收到的消息就是单独的消息了,没有粘包

感谢各位的阅读,以上就是"TCP粘包拆包的概念以及Netty解决TCP粘包拆包实例"的内容了,经过本文的学习后,相信大家对TCP粘包拆包的概念以及Netty解决TCP粘包拆包实例这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是,小编将为大家推送更多相关知识点的文章,欢迎关注!

消息 数据 服务 客户 客户端 内核 系统 操作系统 分隔符 用户 缓冲区 进程 缓冲 实例 概念 两个 大小 字节 空间 设备 数据库的安全要保护哪些东西 数据库安全各自的含义是什么 生产安全数据库录入 数据库的安全性及管理 数据库安全策略包含哪些 海淀数据库安全审计系统 建立农村房屋安全信息数据库 易用的数据库客户端支持安全管理 连接数据库失败ssl安全错误 数据库的锁怎样保障安全 互联网科技股 金融科技创新监管互联网金融 互联网金融与金融科技 互联网科技金融公司 深圳齐家互联网科技 互联网科技宣传片 成都狮子座互联网科技 博兴的互联网电子科技公司 移动互联网年度科技创新 想开互联网科技公司怎么样 阿里互联网科技企业 互联网科技年终奖 本届互联网大会上的黑科技 广州有谷有贝互联网科技有限公司 济南小医互联网科技有限公司招聘 珠海东科互联网科技有限公司 世界互联网科技成果奖 互联网界的黑科技 艺电能源互联网科技有限公司招聘 上海互联网科技园景观 互联网科技活动banner 互联网科技深圳 互联网科技网站 成都玖崟互联网科技有限公司乐商圈怎么样 岳阳华臻互联网科技有限公司 厦门千工互联网科技有限公司 杭州披云加互联网科技有限公司 互联网科技快讯热点 广西泛欧互联网科技有限公司 互联网科技创新创业项目
0