zoukankan      html  css  js  c++  java
  • 粘包问题

    粘包问题

         我们知道,TCP 协议是面向连接的、可靠的、基于字节流的传输层通信协议。其实,TCP作为面向流的协议,不存在“粘包问题”。

        什么是粘包

         其实这里面有两种含义:

         其一是指,由于TCP是面向流的协议,不会按照应用开发者的期望保持send输入数据的边界,导致接收侧有可能一下子收到多个应用层报文,需要应用开发者自己分开。

         其二是指,用户数据被TCP发出去的时候,存在多个小尺寸数据被封装在一个TCP报文中发出去的可能性。这种“粘”不是接收侧的效果,而是由于Nagle算法(或者TCP_CORK)的存在,在发送的时候,就把应用开发者多次send的数据,“粘”在一个tcp报文里面发出去了,于是,先被send的数据可能需要等待一段时间,才能跟后面被send的数据一起组成报文发出去。

         这两个其实都不是“问题”。

         一、针对上面第一点

         1、分析

         1)TCP 协议是基于字节流的传输层协议,其中不存在消息和数据包的概念。

         2)应用层协议没有使用基于长度或者基于终结符的消息边界,导致多个消息的粘连,出现了“粘包”想象。这其实是使用者对于 TCP 的理解有误导致的一个问题。完全可以把这个问题转换为如何设计应用层协议的问题,即如何将TCP流解码为报文数据(stream2datagram)。

        3)数据传递

        为了说清楚这个问题,我们先来看下client/server之间数据传递的过程。

        1、客户端->发送数据

        2、服务端->接收数据

        通常我们直觉性的认为,客户端直接向网络中传输数据,对端从网络中读取数据,但是这是不正确的。

        socket有缓冲区buffer的概念,每个TCP socket在内核中都有一个发送缓冲区和一个接收缓冲区。客户端send操作仅仅是把数据拷贝到buffer中,也就是说send完成了,数据并不代表已经发送到服务端了,之后才由TCP协议从buffer中发送到服务端。此时服务端的接收缓冲区被TCP缓存网络上来的数据,而后server才从buffer中读取数据。

        因此,如果接收数据端的应用层没有及时读取 TCP Recv Buffer 中的数据,就会发生粘包。

        2、解决办法

        最常见的两种解决方案就是基于长度或者基于终结符(Delimiter)。

        二、针对上面第二点

        1、分析

        1)Nagle 算法是一种通过减少数据包的方式提高 TCP 传输性能的算法。

        因为网络 带宽有限,它不会将小的数据块直接发送到目的主机,而是会在本地缓冲区中等待更多待发送的数据,这种批量发送数据的策略虽然会影响实时性和网络延迟,但是能够降低网络拥堵的可能性并减少额外开销。

        Nagle算法如下:

    •  如果包长度达到MSS(或含有Fin包),立刻发送,否则等待下一个包到来;如果下一包到来后两个包的总长度超过MSS的话,就会进行拆分发送;
    •  等待超时(一般为200ms),第一个包没到MSS长度,但是又迟迟等不到第二个包的到来,则立即发送。

          

     

         几十年前还会发生网络拥塞的问题,但是今天的网络带宽资源不再像过去那么紧张,在默认情况下,Linux 内核都会使用如下的方式默认关闭 Nagle 算法:

         TCP_NODELAY = 1

         3)除了 Nagle 算法之外,TCP 协议栈中还有另一个用于延迟发送数据的选项 TCP_CORK,如果我们开启该选项,那么当发送的数据小于 MSS 时,TCP 协议就会延迟 200ms 发送该数据或者等待缓冲区中的数据超过 MSS5。无论是 TCP_NODELAY 还是 TCP_CORK,它们都会通过延迟发送数据来提高带宽的利用率,它们会对应用层协议写入的数据进行拆分和重组,而这些机制和配置能够出现的最重要原因是 — TCP 协议是基于字节流的协议,其本身没有数据包的概念,不会按照数据包发送数据。

         4)其实99%的情况下禁不禁止都一样,延迟根本不是Nagle算法导致的;就算真有问题,最优解决方案也不是屏蔽Nagle算法。

         2、解决办法

         设置TCP_NODELAY来屏蔽Nagle算法。不过这是要看情况的,比如:在频繁进行超短的信息交互(比如几个字节)时禁用Nagle算法就是明智之举。

         不过,就算关闭 Nagle 算法,如果接收数据端的应用层没有及时读取 TCP Recv Buffer 中的数据,还是会发生粘包。

     

         最后,“粘包”这个词语容易引起歧义,不建议沿用。stream2datagram就是stream2datagram,TCP_NODELAY就是TCP_NODELAY。

         参考链接:

         https://www.zhihu.com/question/20210025/answer/1744906223

         https://segmentfault.com/a/1190000039691657

         https://draveness.me/whys-the-design-tcp-message-frame/

  • 相关阅读:
    mongodb的安装和sql操作
    查看apache和nginx的负载和连接数情况
    ansible中playbook使用
    mysql导入导出命令详解
    生产环境下yum的配置
    firewalld的防火墙
    SOCK5代理服务器
    Linux系统基础优化总结
    服务器内存和缓存的优化
    activemq概念介绍
  • 原文地址:https://www.cnblogs.com/hld123/p/15294535.html
Copyright © 2011-2022 走看看