前情提要

四层网络模型各司其职,消息(SDU)在进入每一层时都会多加一个报头(PCI),这个PCI记录着该SDU的一些关键统计信息。SDU+PCI合并起来就组成一个完整的消息,简称为PDU

  1. 链路层:帧(Frame)头部作用

    源 MAC 地址 和 目的 MAC 地址,用于在局域网内通信唯一标识设备,实现数据在物理链路上的传输
  2. 网络层:数据包(Packet)头部作用

    包含 源IP 地址和 目的IP 地址,用户在互联网中实现端到端通信
  3. 传输层:段(Segment)/数据报(Datagram)头部作用

    包含 源端口号 和 目的端口号,用于标识同一主机上的不同应用程序
  4. 应用层:消息(Message)头部作用

    应用层协议根据具体需求定义头部格式,用于实现特定的业务逻辑

详见https://www.cnblogs.com/lmy5215006/p/18838393

数据切片

把互联网比作一条水管,那么这条水管是有一定的粗细的,而水管的粗细决定了流量的大小。因此,当我们发送"Hello World"时,当水管粗时,可以一次性发送完毕,当水管细时,就需要拆解成'He','llo','Wo','rld'。

决定"水管粗细"的由底层的数据链路层决定,为一个MTU(通常为1500Byte),当网络层(IP)数据包<=1500byte时,一个数据包即可完成发送,如果>1500byte就需要拆分为两个数据包。

MTU(Maximum Transmission Unit,最大传输单元)

举个例子:MTU为1500byte,IP头为20Byte,IP层传输了一个3000Byte的数据包

  1. 第一个数据包

    IP Header:20byte

    Payload:1480byte

    Total:1500byte
  2. 第二个数据包

    IP Header:20byte

    Payload:3000byte-1480byte=1520byte (超过MTU,需要再次分片,实际1480)

    Total:1500byte
  3. 第三个数据包

    IP Header:20byte

    Payload:40byte

    Total:60byte

由此可以看到,一次传输被拆分成了三次,每个分配重复携带IP Header,增加了额外的传输冗余,且分配需要重组,增加了延迟与丢包风险

MTU与MSS

为了缓解上述的问题,传输层的TCP协议通过MSS(Maximum Segment Size,最大段大小)来避免IP分片。

  1. 三次握手协商

    三次握手时,双方通过SYN报文交换MSS值,确保Segment 不超过MSS。

    MSS=MTU-IP Header-TCP Header,比如MTU=1500,那么MSS=1500-20-20=1460byte。
  2. 路径MTU发现

    IP协议通过ICMP协议探测传输路径中的最小MTU,动态调整数据包大小以减少分片。

通过动态协商MTU,使得IP数据包使用不会超过MTU的值,从而避免了分片。不会出现MTU=1500,IP数据包3000Byte的现象。

粘包

人生就像打电话,不是你挂就是我挂。

切片也是,你链路层倒是爽了,不用拆包了,但传输层就遭殃了,因为总要有人负重前行。

TCP是一种面向连接,可靠的,基于字节流的传输层通信协议,因为基于字节流的特点,数据由01组成,所以当我们在互联网中传输"hello World"时,是以0101010101这种格式的字节流发送。

这些二进制数据,对于接收端来说,不知道要接收多少才能组成一个消息,因此其本质是应用层消息边界在TCP流中消失

粘包发生的原因如下:

  1. 数据包过小

    当Segment特别小,没有达到MSS的标准,TCP的Nagle算法会原地等待200ms,等下一个包一起发送。
  2. 数据包过大

    如果下一个包来之后,超过了MSS的标准,则会拆包
  3. 接收端读取数据不及时

    接收端处理不及时,导致在Buff中好几个Segment的先后粘在一起,导致接收端无法区分。

说白了,粘包不是TCP的设计缺陷,而是一种取舍。

如何解决粘包

既然原理知道了,那么解决它也很简单.

  1. 消息定长

    每个数据包的长度固定,那么接收端只要固定读取特定长度的二进制流即可区分。
  2. 分割符标记

    通过特殊标记作为头/尾,比如EOF,回车,0xffffff等。当接收端处理到特殊标记时,就知道消息读取完了。
  3. 头部包含长度字段

    一般会配合上面的分割符标记来加强,在Header中加入消息长度,然后就如同消息定长一样读取即可。

如果某个数据里正好有EOF怎么办?

还有标志位作为兜底,发送端在发送时加入16校验和(对完整数据进行CRC),以供接收端校验。

如同文件的MD5一样,下载完成后校验MD5,避免错误。

FAQ

UDP会有粘包问题吗?

不会,原因如下:

  1. UDP 不存在合并数据的机制

    Datagram是独立的数据单元,是最小单位,包含完整的Head和Payload。接收方要设置合理的缓冲区来接收,否则数据会丢失。

    当Datagram,网络层(IP层)会对其分片,但传输层本身不处理分片。
  2. Datagram边界明确

    UDP 协议保证接收方可以通过数据报的长度字段(头部中 Length 字段)准确区分每个数据包的起始和结束。

简单来说,UDP自己都不保证消息消息完整,就算发生粘包又怎么样呢?

网络层会有粘包问题吗?

虽然我们在传输层明确了MSS会小于MTU(1500byte),避免了IP层的大包分片,但还会有漏网之鱼。比如在动态路径MTU发现中,发现某个路由器MTU只有500byte,那么IP层也需要对数据包进行切片。

再此之后会重新协商,传输层会调整MSS为500byte。

回到正题,那么网络层会有粘包问题吗?

答案是不会,再次强调一遍粘包本质是应用层消息边界在TCP流中消失。网络层只负责数据切片以及数据重组,它不关心里面的内容是什么,只是数据的搬运工,因此不会发生粘包。

关闭Nagle算法会减少粘包吗?

关闭Nagle算法会减少粘包,因为小的数据包会立即发送,而不是等200ms。

但治标不治本,接收方处理速度慢也是粘包的一个原因。

C#网络编程(五)----细嗦TCP粘包的更多相关文章

  1. 【游戏开发】网络编程之浅谈TCP粘包、拆包问题及其解决方案

    引子 现如今手游开发中网络编程是必不可少的重要一环,如果使用的是TCP协议的话,那么不可避免的就会遇见TCP粘包和拆包的问题,马三觉得haifeiWu博主的 TCP 粘包问题浅析及其解决方案 这篇博客 ...

  2. Socket编程(4)TCP粘包问题及解决方案

    ① TCP是个流协议,它存在粘包问题 TCP是一个基于字节流的传输服务,"流"意味着TCP所传输的数据是没有边界的.这不同于UDP提供基于消息的传输服务,其传输的数据是有边界的.T ...

  3. python 网络编程(远程执行命令与粘包)

    远程执行命令 先来学习一个新模块 , 一会用到的.. 新模块: subprocess 执行系统命令 r = subprocess.Popen('ls',shell=True,stdout=subpro ...

  4. 网络编程socket模块subprocess模块 粘包的解决

    什么是socket? tcp 可靠地面向连接协议 udp 不可靠的,无连接的服务,传送效率高

  5. TCP粘包问题分析和解决(全)

    TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...

  6. 【转载】TCP粘包问题分析和解决(全)

    TCP通信粘包问题分析和解决(全) 在socket网络程序中,TCP和UDP分别是面向连接和非面向连接的.因此TCP的socket编程,收发两端(客户端和服务器端)都要有成对的socket,因此,发送 ...

  7. Python之路(第三十一篇) 网络编程:简单的tcp套接字通信、粘包现象

    一.简单的tcp套接字通信 套接字通信的一般流程 服务端 server = socket() #创建服务器套接字 server.bind() #把地址绑定到套接字,网络地址加端口 server.lis ...

  8. 网络编程-SOCKET开发之----2. TCP粘包现象产生分析

    1. 粘包现象及产生原因 1)概念 指TCP协议中,发送方发送的若干个包数据到接收方接收时粘成一包.发送方粘包:发送方把若干个要发送的数据包封装成一个包,一次性发送,减少网络IO延迟:接收方粘包:接收 ...

  9. Java网络编程基础之TCP粘包拆包

    TCP是个"流"协议,所谓流,就是没有界限的一串数据.大家可以想象河里的流水,他们是连成一片的,其间并没有分界线.TCP底层并不了解上层业务数据的具体含义,他会根据TCP缓冲区的实 ...

  10. Python全栈-网络编程-TCP粘包

    一.什么是TCP粘包 C/S架构下,接收方不知道每个消息的发送间隙.也不知道每次应该提取多少个字节的数据,与此同时,TCP是面向连接的,面向流的,收发两端都要有,因此发送端为了将多个发往接收端的数据包 ...

随机推荐

  1. 有关算法与数据结构的考题解答参考汇总 [C++] [链表] · 第三篇

    早先年考研的主考科目正是[算法与数据结构],复习得还算可以.也在当时[百度知道]上回答了许多相关问题,现把他们一起汇总整理一下,供读者参考. [1] 原题目地址:https://zhidao.baid ...

  2. CentOS7安装部署ClickHouse(单机版&&集群部署)

    1.1 什么是ClickHouse ClickHouse 是俄罗斯的Yandex于2016年开源的列式存储数据库(DBMS),主要用于在线分析处理查询(OLAP),能够使用SQL查询实时生成分析数据报 ...

  3. VMware虚拟机上安装Kali Linux详细教程

    1.Kali Linux简介 Kali Linux是一个基于Debian的开源Linux发行版,集成了精心挑选的渗透测试和安全审计的工具,供渗透测试和安全设计人员使用,面向各种信息安全任务:如渗透测试 ...

  4. ABC393E题解

    大概评级:绿. 拿到题目,寻找突破口,发现 \(A_i \le 10^6\),一般的数据都是 \(A_i \le 10^9\),所以必有蹊跷. 数学,权值,最大公约数,联想到了因子--懂了,原来是这么 ...

  5. Avalanche公链深度解析:创新共识、亚秒级最终性与生态竞争力

    摘要:Avalanche定位为一个高性能.可扩展的Layer 1区块链平台,但它并不是一个新公链,其主网于2020年9月21日正式上线,有Ava Labs开发.Ava Labs成立于2018年,总部位 ...

  6. # 如何引进高级的 IT 自动化项目:一个 3 步走计划

    如果您的团队与大多数 IT 组织一样,您的团队正在执行某种形式的自动化(包括开发和运营),即使只是运行简单的脚本来完成基本任务. 事实上,开始自动化之旅的最佳地点是执行普通的.低技能的任务,例如密码重 ...

  7. 本地部署overleaf服务帮助latex论文编写

    是的,overleaf是一个很好的服务,提供了立刻上手就可以编写的latex文章的服务.但是,overleaf会面对latex超时,所以需要付钱的情况,这常出现在编写期刊的论文的情况. 因为时效性,所 ...

  8. BUUCTF---还原大师(MD5)

    1.题目 我们得到了一串神秘字符串:TASC?O3RJMV?WDJKX?ZM,问号部分是未知大写字母,为了确定这个神秘字符串,我们通过了其他途径获得了这个字串的32位MD5码.但是我们获得它的32位M ...

  9. IDEA强制注册登录版本号:IntelliJ IDEA 2021.2.2

    建议采用 IntelliJ IDEA 2021.2.2 版本进行  Evaluate for free  试用 IntelliJ IDEA 2021.3.3  以前的版本可以不用注册登录idea账户, ...

  10. JMeter用例数据分离

    1.编写接口用例文件 新建csv文件,以查询用户财富值和时长接口为例 参数说明: ${caseSeq}:用例编号 ${apiType}:api类型 ${apiSeq}:api版本号 ${apiName ...