【linux】网络基础 ---- 传输层

1. UDP协议

(一)UDP协议端格式

注意:

  1. 16位UDP长度, 表示整个数据报(UDP首部+UDP数据)的最大长度
  2. 16位UDP检验和,能判断是否出现数据丢失等问题
  3. 如果校验和出错, 就会直接丢弃

UDP报头本质上也是一个结构体:

操作系统内有多个报头,需要管理 ----- 先描述再组织

(二)UDP 的特点

  • 无连接: 知道对端的IP和端口号就直接进行传输, 不需要建立连接
  • 不可靠: 没有确认机制, 没有重传机制(即使因为校验和出错丢失数据,接收方也不会收到任何信息提示
  • 面向数据报: 不能够灵活的控制读写数据的次数和数量(发送方发送次数就是接收方接收次数,发送方每次发送数据的大小,就是接收方接收数据的大小),也容易导致乱序(数据发送顺序混乱等等)

注意:

UPD 对于 TCP 发送数据的不同:TCP 的读取必须是每次读取一次完整的报文,而不是读取全部数据

(三)面向数据报

应用层交给UDP多长的报文, UDP原样发送, 既不会拆分, 也不会合并

举例,如果用UDP传输100个字节的数据:

如果发送端调用一次sendto, 发送100个字节, 那么接收端也必须调用对应的一次recvfrom, 接收100个

字节,而不能循环调用10次recvfrom, 每次接收10个字节

(四)UDP的缓冲区

  1. UDP没有真正意义上的 发送缓冲区,调用sendto会直接交给内核, 由内核将数据传给网络层协议进行后续的传输动作
  2. UDP具有接收缓冲区. 但是这个接收缓冲区不能保证收到的UDP报的顺序和发送UDP报的顺序一致(乱序问题)
  3. 如果缓冲区满了, 再到达的UDP数据就会被丢弃
  4. UDP的socket既能读, 也能写, 这个概念叫做 全双工

(五)UDP使用注意事项

我们注意到, UDP协议首部中有一个16位的最大长度. 也就是说一个UDP能传输的数据最大长度是64KB(包含UDP首部).

然而64K在当今的互联网环境下, 是一个非常小的数字.

如果我们的数据超过64KB就需要在应用层手动的分包, 将数据分成多个64K大小的数据,分多次发送

(六)基于UDP的应用层协议

NFS: 网络文件系统

TFTP: 简单文件传输协议

DHCP: 动态主机配置协议

BOOTP: 启动协议(用于无盘设备启动)

DNS: 域名解析协议

当然, 也包括你自己写UDP程序时自定义的应用层协议

2. TCP 协议

TCP全称为 "传输控制协议" , 要对数据的传输进行一个详细的控制

(一)TCP协议段格式

注意:

  1. 不管是服务端还是客户端,在 tcp 通信的时候,必须发送的是完整的报头
  2. 为了确保数据的可靠安全,发送方发送了一个完整报文,只有接收到了接受方的回应,这一个过程的发送数据才是可靠的
  3. 无法保证整个 tcp通信时的全过程都是可靠安全的,但是局部通信是可以保证的

  1. 发送方发送数据是会控制速度的(防止接收方的缓冲区满了,从而丢弃数据),主要取决于接收方接收缓冲区剩余空间的大小
  2. 发送方发送数据的顺序,可能和接收方接收数据的顺序不同,从而导致乱序,但是可以根据报文中的32位序号排号(TCP 给每个字节的数据都进行了编号,即序列号,本质就是数组下标,发送的数据块的最后一个字节的下标就是32位序号),恢复原先顺序

  1. 确认序号,填充的是:收到的报文的32位序号 + 1 代表确认序号之前的报文(不包括确认序号),接收方都收到了,下次发送请从确认序号的指定序号发送,这样就允许一些回应的报文丢失了

如 : 收到的报文32文序号是 1000,即 确认序号是 1001,代表0 - 1000 的数据,接收方都接收到了

  1. 设置两个序号 -- 32位序号和确认序号 的好处:可以避免一些情况:比如客户端发送了一个报文,服务端发送给客户端的报文中,既有回应,又有发送的数据,回应可以查看服务端的32位确认序号,发送的数据可以查看服务端的32位序号
  2. tcp 通信的时候,可以建立连接,发送数据,断开连接,这些都需要发送 tcp 报文,因此报文有不同的类型,而服务器要根据不同的报文类型,做出不同的行为,6 位标志可以判断报头的类型
  3. 16位紧急指针:标明该报头的重要数据的起始地址(没有偏移量),所以得到的数据是只有1个字节的,与标志 URG 配合使用
  4. 6 位标志(设置成1代表有效或者需要):
  • URG: 紧急指针是否有效
  • ACK: 确认序号是否有效 (即回应是否有效)
  • PSH: 是否需要提示接收端应用程序立刻从TCP缓冲区把数据读走
  • RST: 是否需要对方重新建立连接。 我们把携带RST标识的称为复位报文段

连接是需要被管理的 --- 先描述再组织

要服务端和客户端都连接上了,才能互发信息,即如果只有一端没有连接,就需要重新连接(三次握手中的数据可能会有丢失现象)

  • SYN: 请求建立连接。我们把携带SYN标识的称为同步报文段
  • FIN: 通知对方, 本端要关闭了

(二)确认应答(ACK)机制

每一个ACK都带有对应的确认序列号, 意思是告诉发送者, 我已经收到了哪些数据,下一次你从哪里开始发

(三)超时重传

发送端发送数据,只有得到回应,才能确定数据是被接收方接收的,但是如果迟迟不回应,有很多原因:发送的数据被丢包,得到的回应的丢失,发送的数据被接收但是仍在接收端的等待队列中等待回应,因此,在一段时间得到不回应的时候,就需要发送端超时重传

发送数据丢包:

回应丢失:

定义超时时间:

  1. 超时重传的时间是动态的,跟网络的速度有关
  2. 时间设定以 500毫秒为单位

动态计算超时时间:

  1. 超时以500ms为一个单位进行控制, 每次判定超时重发的超时时间都是500ms的整数倍.
  2. 如果重发一次之后, 仍然得不到应答, 等待 2*500ms 后再进行重传.
  3. 如果仍然得不到应答, 等待 4*500ms 进行重传. 依次类推, 以指数形式递增.
  4. 累计到一定的重传次数, TCP认为网络或者对端主机出现异常, 强制关闭连接.

(四)连接管理机制

在正常情况下, TCP要经过三次握手建立连接, 四次挥手断开连接

三次握手的必要:

  1. 确保 tcp 通讯的顺畅(双方都能发送和接收数据),即全双工通路流程

  1. 奇数次握手,可以确保一般情况握手失败的情况下,连接成本是由客户端承受的,使得服务端的压力没有那么大

三次握手的作用:

  1. 建立连接
  2. 协商起始序号
  3. 协商双方的接收缓冲区大小

四次挥手:

注意:

  1. 只有两方都发起断连接并且接收到了回应,才是真正的断了连接
  2. 断开连接意味着,双方都没有数据发给对方,所以需要断两次
  3. 如果只有一方断了连接,仍然能接收到另一方发送的数据,因为它只是写端关闭了,不能发送数据(不代表不可以发送报文,报文可以没有有效载荷

完整 tcp 通信流程图:

注意:

  1. 连接建立成功与 accept 函数无关,三次握手都是双方操作系统自动完成的
  2. 当三次握手之后,服务端和客户端的状态都是 ESTABUSHED,此时服务端的连接完成,并且连接是被管理在全连接队列
  3. 当服务端收到客户端的 SYN 时,此时的状态是 SYN_RCVD,它也会维护一个队列是半连接队列,而只有服务端的状态变成 ESTABUSHED 时,才会把半连接队列里的一些连接放入全连接队列里
  4. listen 函数 的第二次参数 backlog 表明的是最多允许 backlog + 1 个节点在全连接队列里(所以服务端的连接是有限的)

  1. 服务端不会长时间维护处于半连接队列的客户端,最后有可能直接被释放掉
  2. 半连接队列维护的节点个数 和 全连接队列维护的节点个数 都是有限的,而全连接维护个数与 listen 函数有关,如果达到上限个数,当再有客户端发起连接的时候,三次握手中的最后一次 ACK被客户端接收到了,但是不做任何反应(状态不会变成 ESTABUSHED
  3. 主动断开连接的一方,最后一次发送完 ACK 后,它的状态 TIME_WAITE 会维持一段时间此时不算完全断开,因此端口号仍然属于这个进程,如果是主动断开的是服务端,再次 bind 可能会发送错误,因为端口号不能被多个进程绑定),才会变成 CLOSED 状态

(五)理解 CLOSE_WAIT 状态

对于服务器上出现大量的 CLOSE_WAIT 状态, 原因就是服务器没有正确的关闭 socket, 导致四次挥手没有正确完成. 这是一个 BUG. 只需要加上对应的 close 即可解决问题.

(六)理解 TIME_WAIT 状态

好处:

  1. 让通信双方把历史数据得以消散
  2. 让断开连接的4次挥手有容错性(如四次挥手中的最后一次 ACM 被丢失,有可能还有时间可以重新发送)

注意:

  1. TCP协议规定,主动关闭连接的一方要处于TIME_ WAIT状态,等待两个MSL的时间后才能回到CLOSED状态.
  2. 使用Ctrl-C 终止了server, 所以server是主动关闭连接的一方, 在TIME_WAIT期间仍然不能再次监听同样的server端口
  3. MSL在 RFC1122 中规定为两分钟,但是各操作系统的实现不同, 在Centos7上默认配置的值是60s
  4. 可以通过 cat /proc/sys/net/ipv4/tcp_fin_timeout 查看MSL的值

TIME_WAIT的时间是2MSL的原因:

  1. MSL是TCP报文的最大生存时间, 因此TIME_WAIT持续存在2MSL的话 ,就能保证在两个传输方向上的尚未被接收或迟到的报文段都已经消失(否则服务器立刻重启, 可能会收到来自上一个进程的迟到的数据, 但是这种数据很可能是错误的);
  2. 同时也是在理论上保证最后一个报文可靠到达(假设最后一个ACK丢失, 那么服务器会再重发一个FIN. 这时虽然客户端的进程不在了, 但是TCP连接还在, 仍然可以重发 ACK)

(七)流量控制

接收端处理数据的速度是有限的. 如果发送端发的太快, 导致接收端的缓冲区被打满, 这个时候如果发送端继续发送, 就会造成丢包, 继而引起丢包重传等等一系列连锁反应.

因此TCP支持根据接收端的处理能力, 来决定发送端的发送速度. 这个机制就叫做流量控制

注意:

  1. 第一次发送的时候,如何确认发送数据的大小是合理的 -- 3次握手中,双方交换了报文,知道对方的接收能力
  2. 流量控制,给 tcp 通信带来了可靠性(丢包减少),间接也提高了效率(超时重传少了)

控制过程:

  • 接收端将自己可以接收的缓冲区大小放入 TCP 首部中的 "窗口大小" 字段, 通过ACK端通知发送端;
  • 窗口大小字段越大, 说明网络的吞吐量越高
  • 接收端一旦发现自己的缓冲区快满了, 就会将窗口大小设置成一个更小的值通知给发送端
  • 发送端接受到这个窗口之后, 就会减慢自己的发送速度
  • 如果接收端缓冲区满了, 就会将窗口置为0 , 这时发送方不再发送数据, 但是需要定期发送一个窗口探测数据段, 使接收端把窗口大小告诉发送端

接收端把窗口大小告诉发送端,根据TCP首部中有一个16位窗口字段(就是存放了窗口大小信息),16位数字最大表示65535, 但是TCP窗口最大不是65535字节

实际上, TCP首部40字节选项中还包含了一个窗口扩大因子M, 实际窗口大小是 窗口字段的值左移 M 位

(八)滑动窗口

每一个发送的数据段, 都要给一个ACK确认应答,收到ACK后再发送下一个数据段

这样做有一个比较大的缺点, 就是性能较差, 尤其是数据往返的时间较长的时候

所以真正通信的策略是:一次发送多条数据, 就可以大大的提高性能(其实是将多个段的等待时

间重叠在一起了)

现在又有的一个问题是,如果发送报文丢失,就会导致超时重发,那么发送端是如何维护需要超时重发的数据呢?

注意:

  1. 滑动窗口是发送缓冲区的一部分
  2. 滑动窗口的范围是和对方接收窗口的大小有关
  3. 发送缓冲区的区域划分:通过指针/下标来进行区分

从目前看来,滑动窗口的大小不能超过接收方的接收大小,即窗口大小(具体规定在拥塞控制)

深刻理解滑动窗口原理操作,如图:

情况一:如果接收方接收到了报文,回应 ACK 丢失

情况二:如果发送方的报文丢失

注意:

  1. 这种情况下,还涉及到了快重传:如果三次确认序号都是一样的(说明出现丢包),此时不采取超时重传,而是发送端立刻补发报文,这种策略就叫快重传
  2. 快重传提高了效率,减少了重传次数
  3. 快重传和超时重传都是必要的,当等待发送的报文少于三个,如果发送端发生报文丢失,此时采取的是超时重传

滑动窗口移动和大小变化问题:

  1. 滑动窗口的两个指针都是向右移动的
  2. 滑动窗口的大小是动态变化的
  3. 滑动窗口的大小可能为0,意味着没有带发送的报文,或者接收方的窗口大小为0(应用层没从内核里读取数据)
  4. 滑动窗口像一个环形的数组,在发送缓冲区中不会越界

(九)延迟应答

为了提高 tcp通信 效率,窗口大小要尽可能大,而使得窗口大小可能增大,我们有时候采取延迟应答,即接收端接收到报文后,不会立刻发送ACK应答,而是晚一点(这个动作只是在 tcp协议 这一层发生,与应用层无关,应用层仍然可以读取数据)

延迟发送的时间是远小于超时重传的时间,在延迟的时间内,有可能应用层读取了数据,使得窗口大小变大

采取延迟应答的可能时期:

  • 数量限制: 每隔N个包就应答一次;
  • 时间限制: 超过最大延迟时间就应答一次;

具体的数量和超时时间, 依操作系统不同也有差异, 一般N取2, 超时时间取200ms

(十)捎带应答

捎带应答:

报文在 ACM应答 的同时,也携带了数据

好处:

减少了报文的个数,提高了效率

(十一)阻塞控制

发送报文的过程出现了问题,不仅仅可能是双方主机的问题,也可能是网络的问题

发送报文丢失情况:

  1. 少量报文丢失 ---- 正确情况
  2. 大量报文丢失 ---- 网络出问题(有很多原因,可能是硬件,也可能是发送报文数据量过大,引起阻塞)

判断报文丢失是网络的问题:

如果出现大量报文超时重传,则表明大量报文丢失,网络出现问题

注意:

  1. 如果网络出现了问题,发送端的主机基本都能识别出来(少量的发送端发送报文少,可能不会意识到网络问题),达成一种共识,并进行相应措施
  2. 如果网络出现了问题,发送主机不能再像之前一样,把所有超时报文都重传(如果都超时重传,网络中的报文数量只会更多,仍然会有大量报文丢失),而是试探网络传输中可以接收的报文个数

试探网络传输中可以接收的报文个数,举例:

可以一开始只发送一个报文,得到应答之后

尝试发送两个报文,都得到应答之后

再尝试发送 n 个报文(指数增长),以此来判断此时网络中可以接收的报文的最大个数(拥塞窗口

注意:

  1. TCP引入 慢启动 机制, 先发少量的数据, 探探路, 摸清当前的网络拥堵状态, 再决定按照多大的速度传输数据
  2. 像上面这样的拥塞窗口(是动态的)增长速度, 是指数级别的,"慢启动" 只是指初使时慢, 但是增长速度非常快
  3. 为了不增长的那么快, 因此不能使拥塞窗口单纯的加倍,此处引入一个叫做慢启动的阈值 ,当拥塞窗口超过这个阈值的时候, 不再按照指数方式增长, 而是按照线性方式增长
  4. 发送数据个数不可能一直增长,会受到限制

纠正滑动窗口的大小:

left = 确认序号

right = min(窗口大小,有效数据,拥塞窗口)

(十二)粘包问题

粘包问题中的 "包" , 是指的应用层的数据包,因为数据从内核里面是全部读出来的,导致报文可能不完整,多出来的数据包就是发送了粘包问题

解决粘包问题,需要明确每一个报文之间的边界

解决粘包问题:

  1. 定长报文
  2. 特殊字符分隔
  3. 使用自描述字符 + 定长报头
  4. 使用自描述字符 + 特殊字符

(十三)TCP异常情况

进程终止: 进程终止会释放文件描述符, 仍然可以发送FIN. 和正常关闭没有什么区别

机器重启: 和进程终止的情况相同

机器掉电/网线断开: 接收端认为连接还在, 一旦接收端有写入操作, 接收端发现连接已经不在了, 就会进行reset重新连接,并且接收方也会定期询问对方是否还使用写入操作,如果对方不在 , TCP自己也内置了一个保活定时器 , 也会把连接释放

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/13589.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

软件包管理

软件安装 软件包管理器 APT(Advanced Package Tool): 发行版:主要用于 Debian 及其衍生版(如 Ubuntu)。 常用命令: apt-get install :安装软件包。 apt-get update:更新…

[项目代码] YOLOv5 铁路工人安全帽安全背心识别 [目标检测]

YOLOv5是一种单阶段(one-stage)检测算法,它将目标检测问题转化为一个回归问题,能够在一次前向传播过程中同时完成目标的分类和定位任务。相较于两阶段检测算法(如Faster R-CNN),YOLOv5具有更高的…

Linux逻辑卷

文章目录 逻辑卷 🏡作者主页:点击! 🤖Linux专栏:点击! ⏰️创作时间:2024年11月12日11点09分 逻辑卷 LVM逻辑卷管理是Linux环境中对磁盘分区进行管理的一种机制,建立在硬盘和分区之…

【设计模式】创建型设计模式-工厂模式的实现

工厂模式实现 定义例子UML类图理解Java代码实现总结 定义 工厂方法模式定义了一个接口用于创建对象,该模式由子类决定实例化哪个工厂类。该模式把类的实例化推迟到了子类。 例子 通过一个公共的类方法来管理画图对象的创建。 UML类图理解 Java代码实现 定义接口…

Spring Boot实战:编程训练系统开发手册

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及,互联网成为人们查找信息的重要场所,二十一世纪是信息的时代,所以信息的管理显得特别重要。因此,使用计算机来管理编程训练系统的相关信息成为必然。开发合适…

方案丨车险保单OCR:3秒钟完成保单审核

在涉及车辆交易的各种情况下,记录和管理车险保单信息是一项必不可少的任务。然而,面对数量庞大的电子保单,传统的手工录入方式显得尤为低效——它不仅消耗大量时间,而且容易出现错误,这不仅影响了用户的满意度&#xf…

性能测试|JMeter接口与性能测试项目

前言 在软件开发和运维过程中,接口性能测试是一项至关重要的工作。JMeter作为一款开源的Java应用,被广泛用于进行各种性能测试,包括接口性能测试。本文将详细介绍如何使用JMeter进行接口性能测试的过程和步骤。 JMeter是Apache组织开发的基…

嵌入式硬件电子电路设计(五)MOS管详解(NMOS、PMOS、三极管跟mos管的区别)

引言:在我们的日常使用中,MOS就是个纯粹的电子开关,虽然MOS管也有放大作用,但是几乎用不到,只用它的开关作用,一般的电机驱动,开关电源,逆变器等大功率设备,全部使用MOS管…

如何优化开放数据湖仓一体的性能

数据湖仓一体架构由 Apache Hudi、Apache Iceberg 和 Delta Lake 等开放表格式提供支持,提供了一种开放且经济高效的方式来管理组织不断增长的数据和分析需求。它提供了在同一数据存储上运行并发事务的可靠性,从而提高了效率。数据湖仓一体支持关键功能&…

比较基因组分析

比较基因组分析(Comparative Genomics Analysis)是一门通过比较不同物种或个体的基因组序列来研究其相似性与差异性的科学方法。它有助于揭示物种间的进化关系、基因功能、生物适应性及潜在的疾病机制。近年来,随着高通量测序技术的发展&…

leetcode 148. 排序链表 中等

给你链表的头结点 head ,请将其按 升序 排列并返回 排序后的链表 。 示例 1: 输入:head [4,2,1,3] 输出:[1,2,3,4] 示例 2: 输入:head [-1,5,3,4,0] 输出:[-1,0,3,4,5]示例 3: …

基于单片机的智能小车(论文+源码)

1系统整体方案 此次多功能智能小车的设计系统,其整个控制电路的框架如下图所示。整个系统采用STM32单片机为控制器其中:LCD液晶负责显示当前信息,蜂鸣器负责特殊情况下进行报警提醒,红外遥控模块方便用户进行远程操作小车,电机模块拟采用前驱的方式&…

基于matlab的CNN食物识别分类系统,matlab深度学习分类,训练+数据集+界面

文章目录 前言🎓一、数据集准备🎓二、模型训练🍀🍀1.初始化🍀🍀2.加载数据集🍀🍀3.划分数据集,并保存到新的文件夹🍀🍀4.可视化数据集🍀🍀5.模型构建🍀🍀6.数据增强🍀🍀7.设置训练参数🍀🍀8.训练与测试🎓三、模型测试🍀🍀1.初始化�…

UCSD:LLM通过工具使用解决科学问题

📖标题:Adapting While Learning: Grounding LLMs for Scientific Problems with Intelligent Tool Usage Adaptation 🌐来源:arXiv, 2411.00412 🌟摘要 🔸大型语言模型(LLMs)在解…

【时间之外】IT人求职和创业应知【34】-人和机器人,机器人更可靠

目录 新闻一:人形机器人产业持续高速增长,2026年中国市场规模将突破200亿元 新闻二:AI技术驱动设备厂商格局变化,部分厂商市占率快速提升 新闻三:华为与江淮汽车携手打造超高端品牌“尊界”,计划于明年春…

MyBatis及相关文件配置

MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。以下是对MyBatis的详细讲解: 一、MyBatis的起源与发展 MyBatis最初是Apache的一个开源项目iBATIS,2010年迁移到Google Code并改名为MyBatis,2013年11月又…

【FastAPI】1-url参数

fastapi的核心功能是提供HTTP请求接口 “幂等”和“非幂等” 幂等(idempotent):如果一个方法重复执行多次,产生的效果是一样的,那么这个方法就是幂等的 “Methods can also have the property of “idempotence” in …

CentOS Stream 9设置静态IP

CentOS Stream 9设置静态IP CentOS Stream 9作为CentOS Stream发行版的下一个主要版本,已经发布有一段时间,但与目前广泛使用的CentOS7有较大区别。安装试用Stream 9的过程中,就发现设置静态IP的方式和CentOS7/8差别较大,在此记录…

机器人学 雅可比矩阵

雅可比矩阵(Jacobian Matrix)是机器人学中一个非常重要的工具,广泛应用于分析机器人末端执行器的速度和力学(静力)关系。理解雅可比矩阵的速度和静力作用对于机器人运动控制、动力学分析以及优化设计具有重要意义。 一…

【蓝桥等考C++真题】蓝桥杯等级考试C++组第13级L13真题原题(含答案)-最小的数

CL13 最小的数(20 分) 输入一个有 n 个无重复元素的整数数组 a&#xff0c;输出数组中最小的数。提示&#xff1a;如使用排序库函数 sort()&#xff0c;需要包含头文件#include 。输入&#xff1a; 第一行一个正整数 n(2<n<20)&#xff1b; 第二行 n 个不重的整数 a[i]…