TCP中的粘包、拆包问题产生原因及解决方法

简介: TCP中的粘包、拆包问题产生原因及解决方法

 目录

粘包/拆包 问题产生原因:

解决 粘包/拆包 问题:

为什么TCP有粘包?

为什么UDP没有粘包?

发生在网络的哪些层上?


粘包/拆包 问题产生原因:

发生TCP粘包或拆包有很多原因,现列出常见的几点:

    1. 要发送的数据大于TCP发送缓冲区剩余空间大小,将会发生拆包。
    2. 待发送数据大于MSS(TCP报文长度 - TCP头部长度 > MSS最大报文长度),TCP在传输前将根据MSS大小进行拆包分段发送。
    3. 要发送的数据小于TCP发送缓冲区的大小,TCP将多次写入缓冲区的数据包合并为一次发送,将发生粘包(Nagle算法优化,避免tcp报文头重脚轻的情况发生)
    4. 接收数据端的应用层没有及时读取接收缓冲区中的数据,将发生粘包。

    补充:

    MTU 最大传输单元 (链路层):

    数据链路层传输的帧大小是有限制的,不能把一个太大的包直接塞给链路层,这个限制被称为「最大传输单元(Maximum Transmission Unit, MTU)」

    以太网的帧最大的帧是 1518 字节,除去 14 字节头部和 4 字节 CRC,有效荷载最大为 1500,这个值就是以太网的 MTU。

     

    MSS TCP最大段大小 Maximum Segment Size(传输层,可传输tcp数据包大小):

    TCP 为了避免被发送方分片,会主动把数据分割成小段再交给网络层,最大的分段大小称之为 MSS(Max Segment Size)。

    这样一个 MSS 的数据恰好能装进一个 MTU 而不用分片。

    一般,在以太网中 TCP 的 MSS = 1500(MTU) - 20(IP 头大小) - 20(TCP 头大小)= 1460

    image.gif编辑

    总结:IP 数据包长度超过链路的 MTU 时,在发送之前需要分片,而 TCP 层为了 IP 层不用分片主动将包切割成 MSS 大小。

    解决 粘包/拆包 问题:

    解决问题关键在于如何给每个数据包添加边界信息用于区分不同数据包

      1. 消息分为tcp首部和tcp消息体,tcp首部中应保存数据包的长度TCP的首部原本是没有表示数据长度的字段,因为可由IP层计算出:TCP数据包长度 = IP首部的数据包长度 - IP首部长度(20字节 )- TCP包首部长度(20字节 )。这样接收端在接收到数据后,通过读取包首部的长度字段,就知道每个数据包的实际长度了。
      2. 发送端在每个包的末尾使用固定的分隔符(如 \r\n),这样接收端通过这个边界就可以将不同的数据包拆分开(如 FTP协议)。
      3. 发送端将每个数据包封装为固定长度(不够的可以通过补0填充),这样接收端每次从接收缓冲区中读取固定长度的数据就自然而然的把每个数据包拆分开来。不推荐这种方式,尤其是在高并发大流量的业务场景下,会消耗不必要的资源...

      补充:TCP报文格式图

      image.gif编辑

      为什么TCP有粘包?

      TCP协议粘包拆包问题是因为TCP协议数据传输是基于 "字节流" 的,它不包含消息、数据包等概念,需要应用层协议自己设计消息边界。日常网络应用开发大都在传输层进行,因此粘包拆包问题大都只发生在TCP协议中。

      为什么UDP没有粘包?

        1. UDP有消息保护边界,不会发生粘包拆包问题
        2. UDP发送的时候,不经过Nagle算法优化,不会将多个小包合并一次发送出去。
        3. UDP协议的接收端,采用了链式结构来记录每一个到达的UDP包。UDP是基于报文发送的,从UDP的帧结构可以看出,在UDP首部采用了16bit字段来指示UDP数据报文的长度,因此在应用层能很好的将不同的数据报文区分开,从而避免粘包和拆包的问题。

        补充:"消息保护边界",就是指传输协议把数据当作一条独立的消息在网络上传输,接收端只能接收独立消息。也就是说存在保护消息边界,接收端一次只能接收发送端发出的一个数据包。而面向字节流是指无消息保护边界的,如果发送端连续发送数据,接收端有可能在一次接收动作中,会接收两个或者更多的数据包。

        发生在网络的哪些层上?

        粘包拆包问题在数据 “链路层、网络层、传输层” 都可能发生。

        目录
        相关文章
        |
        消息中间件 存储 网络协议
        从零开始掌握进程间通信:管道、信号、消息队列、共享内存大揭秘
        本文详细介绍了进程间通信(IPC)的六种主要方式:管道、信号、消息队列、共享内存、信号量和套接字。每种方式都有其特点和适用场景,如管道适用于父子进程间的通信,消息队列能传递结构化数据,共享内存提供高速数据交换,信号量用于同步控制,套接字支持跨网络通信。通过对比和分析,帮助读者理解并选择合适的IPC机制,以提高系统性能和可靠性。
        2133 14
        |
        缓存 移动开发 网络协议
        为什么会TCP粘包?读完这篇你就懂了
        在网络编程中,TCP粘包问题指发送方多个数据包在接收方粘成一包,导致数据解析混乱。其原因包括Nagle算法合并小包、发送方收集多个小分组及接收方缓存积压等。解决方法有:固定消息长度、包尾加特殊标记(如\r\n)、包头加包体长度等。选择合适方案可确保数据传输的可靠性和准确性。
        |
        网络协议 算法 程序员
        第十问:TCP协议是怎么做到可靠性的?它的可靠指的是到哪一层的可靠?
        TCP(传输控制协议)是一种面向连接的传输层协议,其核心特性是可靠性。TCP通过数据分片与排序、确认机制(ACK)、超时重传、流量控制、拥塞控制、校验和等机制,确保数据从发送方到接收方的完整性和有序性。这些机制共同作用,使TCP能够在复杂网络环境中实现稳定的数据传输。TCP的可靠性主要指的是从传输层到传输层的可靠性,传输层之上的可靠性则由应用程序负责。
        |
        域名解析 存储 缓存
        深入学习 DNS 域名解析
        在平时工作中相信大家都离不开 DNS 解析,因为 DNS 解析是互联网访问的第一步,无论是使用笔记本浏览器访问网络还是打开手机APP的时候,访问网络资源的第一步必然要经过DNS解析流程。
        1389 31
        |
        存储 编译器 C++
        【c++】多态(多态的概念及实现、虚函数重写、纯虚函数和抽象类、虚函数表、多态的实现过程)
        本文介绍了面向对象编程中的多态特性,涵盖其概念、实现条件及原理。多态指“一个接口,多种实现”,通过基类指针或引用来调用不同派生类的重写虚函数,实现运行时多态。文中详细解释了虚函数、虚函数表(vtable)、纯虚函数与抽象类的概念,并通过代码示例展示了多态的具体应用。此外,还讨论了动态绑定和静态绑定的区别,帮助读者深入理解多态机制。最后总结了多态在编程中的重要性和应用场景。 文章结构清晰,从基础到深入,适合初学者和有一定基础的开发者学习。如果你觉得内容有帮助,请点赞支持。 ❤❤❤
        1560 0
        |
        消息中间件 中间件 Kafka
        分布式事务最全详解 ,看这篇就够了!
        本文详解分布式事务的一致性及实战解决方案,包括CAP理论、BASE理论及2PC、TCC、消息队列等常见方案,助你深入理解分布式系统的核心技术。关注【mikechen的互联网架构】,10年+BAT架构经验倾囊相授。
        分布式事务最全详解 ,看这篇就够了!
        |
        网络协议 算法 网络性能优化
        |
        存储 安全 程序员
        【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
        【C++篇】深入内存迷宫:C/C++ 高效内存管理全揭秘
        980 3
        |
        存储 缓存 负载均衡
        图解一致性哈希算法,看这一篇就够了!
        近段时间一直在总结分布式系统架构常见的算法。前面我们介绍过布隆过滤器算法。接下来介绍一个非常重要、也非常实用的算法:一致性哈希算法。通过介绍一致性哈希算法的原理并给出了一种实现和实际运用的案例,带大家真正理解一致性哈希算法。
        29816 66
        图解一致性哈希算法,看这一篇就够了!
        |
        存储 监控 Linux
        select/poll/epoll优缺点及应用场景
        select/poll/epoll优缺点及应用场景
        768 1