当前位置: 首页 > article >正文

【计算机网络】传输层协议TCP

目录

  • 一、重新理解封装和解包
  • 二、TCP协议段格式
  • 三、确认应答(ACK)机制
  • 四、超时重传机制
  • 五、连接管理机制
  • 六、理解TIME_WAIT状态和CLOSE_WAIT状态
  • 七、流量控制
  • 八、滑动窗口
  • 九、拥塞控制
  • 十、延迟应答
  • 十一、面向字节流
  • 十二、粘包问题

一、重新理解封装和解包

在网络协议栈中(数据链路层、网络层、传输层),操作系统可能会收到很多报文,有的报文还没有处理,可能还没到传输层,在网络层或者链路层。操作系统要对这些报文进行管理,先描述,再组织。
在这里插入图片描述
定义描述报头属性的结构体对象,里面的指针指向内存空间的区域代表报头和数据存储的位置。封装:指向报头的指针往前移动;解包:指向报头的指针往后移动。所以封装和解包本质是指针在内存空间的移动。

二、TCP协议段格式

TCP 全称为 “传输控制协议(Transmission Control Protocol”),下面是TCP 协议段格式
在这里插入图片描述
报头和有效载荷如何分离?
提取固定的20字节长度和首部长度,4位首部长度是60(【0,15】,基本单位是4字节),报头是20。如果首部长度就是20,报头读完就是数据;如果首部长度大于20,减去20,再把多出来的读取出来,剩下的就是数据正文。

6位标志位:

  • URG:紧急指针是否有效
  • ACK:确认号是否有效
  • PSH:提示接收端应用程序立刻从TCP缓冲区把数据读走
  • RST:对方要求重新建立连接;我们把携带RST标识的称为复位报文段
  • SYN:请求建立连接;我们把携带SYN标识的称为同步报文段
  • FIN:通知对方,本端要关闭了,我们称携带FIN标识的为结束报文段

三、确认应答(ACK)机制

TCP如何保证可靠性?
在这里插入图片描述
客户端给服务端发送数据,服务端应答;服务端发送给客户端,客户端应答。如果一方没有收到应答,说明可能这个过程中有丢包,否则既能发送数据收到应答,又能接收数据应答另一方,就能保证传输过程的可靠性。发送数据和发送应答,一般是双方的操作系统自动完成的。

客户端可以给服务端发送多次数据,服务端多次应答回去
在这里插入图片描述
有一个问题:如何确定发送和应答是对应的?序号和确认序号。根据序号,进行tcp的按序到达;确认序号是收到序号的+1,就能保证发送和应答是对应的关系。

注意:发送的数据是有带报头的

为什么要有序号和确认序号两个,一个序号不能解决问题?
在这里插入图片描述
因为在传输的过程中,有可能发送的消息既是数据又是应答,这种情况叫捎带应答。如果只有一个序号,就很难确定哪个是数据哪个是应答,因此才要有序号和确认序号共同解决。

序号的意义:按序到达,应答和确认对应
确认序号&&序号:向对方发送信号的同时,也在作应答

为什么tcp报头要有标志位?
在数据传输的时候有建立连接的报文、正常通信的报文、断开连接的报文,总之就是通信过程中会收到各种各样的报文,而TCP报文是需要类型的,所以要区分报文的类型,标志位存在的意义就是区分不同的TCP报文类型。

初步认识三次握手,四次挥手:

三次握手:
在这里插入图片描述
客户端发起连接,标志位SYN置为1,服务端调用监听方法listen,收到信息,也发起连接并确认应答,标志位ACK也置为1,然后客户端向服务端发送应答(可能有数据),服务端接收。

注意:每次发送都是一个完整的报头

四次挥手:
在这里插入图片描述
客户端取消连接,标志位FIN置为1,服务端接收,发送应答。同样,服务端也要取消连接,客户端接收,发送应答。

如果只是应答,就是ACK;如果是稍等应答,即发送应答的同时又发送数据,那么就是ACK+数据。

四、超时重传机制

如果客户端发送数据,客户端没有收到服务端的应答,有两种可能:一是丢包了,服务端没有收到数据;二是服务端收到了数据,但是还没有收到应答。在收到应答前,客户端是需要等的,等也是有时间范围,如果超过了一个时间段还没收到应答,就判定超时了,需要重传,这个过程也叫超时重传。

超时的时间如何确定?

  • 最理想的情况下,找到一个最小的时间,保证"确认应答一定能在这个时间内返回
  • 但是这个时间的长短,随着网络环境的不同,是有差异的
  • 如果超时时间设的太长,会影响整体的重传效率
  • 如果超时时间设的太短,有可能会频繁发送重复的包

TCP为了保证无论在任何环境下都能比较高性能的通信,因此会动态计算这个最大超时时间

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

五、连接管理机制

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

三次握手中,connect是发起连接的,accept是接收数据,并返回新的文件描述符,不参与三次握手。三次握手不能百分之百建立连接,而是经历了三次握手,让客户端和服务端认为已经建立好了连接。
在这里插入图片描述
因为最后客户端发送应答,服务端不一定能够接收。为什么?怎么知道服务端可能没有接收?因为在前面两次的握手中,客户端发送信息,建立连接,服务端应答,客户端收到服务端的应答,才确定之前发送的信息服务端是收到。而最后一次是单向的,服务端没有应答,所以客户端发送的过程中有存在丢包的可能。既然存在这个风险,那如何解决?其实不用担心,因为建立成功概率比较高,一般前两次连接好了,最后一次大概率也是成功的。

假设最后一次的ACK不成功,即客户端发送ACK后服务端没有收到,但是客户端会认为服务端收到了(其实没有),那么客户端自己就确定已经建立好连接了,而服务端还没有建立好连接。服务端会进行等待,然后超时重传。但是客户端可能很着急,直接就给服务端发送数据了,可是服务端连接还没建立好,怎么办?告知客户端要重置,通过RST标志位,把它置为1,连接重置就是重新进行三次握手。总结:RST是用来处理建立连接出现异常的问题。

为什么建立连接是三次握手?

  • 需要保证通信是健康的。让客户端和服务端双方,都会有确定的一次收发,确认全双工
  • 确保双方操作系统是健康且愿意通信的

为什么要进行四次挥手?
理由同三次握手,因为它们的原理类似。这里要说明的是发送数据的情况。

客户端close取消连接时,不会发送用户数据了,服务端会应答;然后服务端也close,客户端应答。有一个问题,不是说客户端不能发数据了吗?为什么还能发送ACK(哪方close了,就不能发送数据)?因为ACK发送的只是报头,不包含数据,用来确实应答的。

既然客户端close后不能发送数据了,服务端应答,如果服务端没有很着急close,那么在服务端Close之前这段时间,服务端可以向客户端发送数据。发送完数据,服务端再close,然后客户端应答。又有一个问题了,客户端都已经取消连接,就算收到数据怎么交给上层呢?解决方法:shutdown系统调用
在这里插入图片描述
如果服务端应答与close是比较靠近发生的,那么可以既发生ACK又发送FIN,这叫捎带应答。也可以看作是三次挥手。

六、理解TIME_WAIT状态和CLOSE_WAIT状态

客户端断开连接,服务端应答,但是服务端还没close,此时服务端处于CLOSE_WAIT状态。客户端断开连接之后,在它确定成功于服务端断开连接之前,处于TIME_WAIT状态。

如果服务器有大量的CLOSE_WAIT状态时,大概率是因为服务器写的有bug,主要是没有close。

TIME_WAIT 状态引起的 bind 失败,因为一方主动断开连接,而另一方没有应答或者也断开连接,就会产生大量的TIME_WAIT状态,解决方法:setsockopt

七、流量控制

发送端给接收端发送数据,接收端处理数据的速度是有限的,如果发送端发的太快,接收端的接收缓冲区来不及处理,就会造成丢包;如果发的太慢,导致发送的效率低。因此 TCP 支持根据接收端的处理能力,来决定发送端的发送速度,这个机制就叫做流量控制(Flow Control)。

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

八、滑动窗口

主机A给主机B发送数据,可以一条一条发,但是发送效率低
在这里插入图片描述
可以多条发送,提高了效率
在这里插入图片描述
发送多条消息,暂时不需要应答,可以直接发送数据,在主机A一个“窗口大小”中,所谓窗口大小的窗口,就是滑动窗口。滑动窗口指的是无需等待确认应答而可以继续发送数据的最大值;滑动窗口的大小是对方缓冲区中剩余空间的大小(注意:对方缓冲区的剩余空间大小是会变化的)

滑动窗口在发送缓冲区中如下图,分为3个部分(其实是4个)
在这里插入图片描述
滑动窗口支持超时重传。对发送的报文,并且没有收到应答的报文进行保存,方便我们进行重传。保存在哪?滑动窗口。

滑动窗口只能向右移动,可以变大变小,可以为0;发送端收到ACK报文,应答报头中的窗口大小,表明对方的接受能力,这时候要变更滑动窗口。

有三个问题:最左侧丢包、中间报文丢失、最右侧丢包

最左侧丢包:
在这里插入图片描述
接受端发送确认应答,是数据的确认序号,确认序号的意义:确认序号之前的报文全部都收到了。如果确认应答是2001,说明1到1000和1001到2000都收到;如果2001到5000中有一段没收到,比如2001到3000,那么确认序号还是2001发送给发送端,此时滑动窗口是不能右移的。

在这里插入图片描述
如果有三次重复的确认应答,那么发送端就会进行重发,这叫做快重传。假如中间还有其他的数据段丢包了怎么办?没有关系,因为发送的过程的连续的,快重传补好了1001到2000,后面的也会开始补。

有了快重传为什么还有超时重传?快重传的效率会高些,但是不能保证所有的数据都能发送成功,而超时重传可以对快重传进行补漏,相当于兜底。

中间报文丢失、最右侧丢包最终都会转换为最左侧丢包。 以中间丢包为例,滑动窗口右移,然后原来的中间丢包,变成了最左侧丢包。最右侧丢包也是如此。

数据包已经抵达,ACK被丢了怎么办?这种情况下,部分ACK丢了并不要紧,因为可以通过后续的ACK进行确认。

前面学的流量控制中,让发送端调节发送的速度,如何调节:具体的细节就是滑动窗口。

九、拥塞控制

虽然TCP有滑动窗口,能够高效可靠的发送大量数据,但是如果在刚开始的阶段就发送大量数据,可能会引发网络拥塞。

一旦判定为网络拥塞,是不能立即重传的。解决方法:拥塞控制。

TCP引入慢启动机制,先发少量的数据,探探路摸清当前的网络拥堵状态,再决定按照多大的速度传输数据。

拥塞窗口:每次发送数据包的时候,将拥塞窗口和接收端主机反馈的窗口大小做比较,取较小的值作为实际发送的窗口(滑动窗口=min(拥塞窗口,接收方窗口-接受能力))

拥塞窗口增长速度是指数级别的:
在这里插入图片描述

少量的丢包, 我们仅仅是触发超时重传; 大量的丢包, 我们就认为网络拥塞

十、延迟应答

发送端发生一段报文,接收端可以先不用那么着急就应答,可以等一会,再应答,这叫做延迟应答。

为什么?在等待的过程中,接收端的接收缓冲区里面的数据更多交给上层处理,即接收缓冲区空间更大了,这时再应答既可以确认前面的收到的信息,又能通知发送端可以发送更大的信息,提高了发送效率。

窗口越大, 网络吞吐量就越大, 传输效率就越高。在保证网络
不拥塞的情况下尽量提高传输效率

所有的包都可以延迟应答吗?不能。

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

具体的数量和超时时间, 依操作系统不同也有差异。

十一、面向字节流

创建一个 TCP 的 socket,同时在内核中创建一个 发送缓冲区 和一个 接收缓冲区

  • 调用 write 时,数据会先写入发送缓冲区中
  • 如果发送的字节数太长,会被拆分成多个 TCP 的数据包发出
  • 如果发送的字节数太短,就会先在缓冲区里等待,等到缓冲区长度差不多了,或者其他合适的时机发送出去
  • 接收数据的时候,数据也是从网卡驱动程序到达内核的接收缓冲区
  • 然后应用程序可以调用 read 从接收缓冲区拿数据

由于缓冲区的存在, TCP 程序的读和写不需要一一匹配
写 100 个字节数据时, 可以调用一次 write 写 100 个字节, 也可以调用 100 次write, 每次写一个字节
读 100 个字节数据时, 也完全不需要考虑写的时候是怎么写的, 既可以一次read 100 个字节, 也可以一次 read 一个字节, 重复 100 次

十二、粘包问题

什么是粘包?

  • 粘包问题中的 “包” ,是指的应用层的数据包
  • 在 TCP 的协议头中,没有如同 UDP 一样的 “报文长度” 这样的字段,但是有一个序号这样的字段
  • 站在传输层的角度,TCP 是一个一个报文过来的,按照序号排好序放在缓冲区中
  • 站在应用层的角度,看到的只是一串连续的字节数据
  • 应用程序看到了这么一连串的字节数据,就不知道从哪个部分开始到哪个部分是一个完整的应用层数据包

如何解决粘包问题?明确两个包之间的边界

  • 对于定长的包,保证每次都按固定大小读取即可
  • 对于变长的包,可以在包头的位置,约定一个包总长度的字段,从而就知道了包的结束位置;还可以在包和包之间使用明确的分隔符

对于 UDP 协议来说,是否也存在 “粘包问题” 呢?

  • UDP如果还没有上层交付数据,它的的报文长度仍然在,同时,UDP 是一个一个把数据交付给应用层,所以有很明确的数据边界
  • 站在应用层的站在应用层的角度,使用 UDP 的时候,要么收到完整的 UDP 报文,要么不收,不会出现"半个"的情况

http://www.kler.cn/a/321857.html

相关文章:

  • HarmonyOS本地存储-Preferences(用户首选项)的使用
  • websocket初始化
  • DAY112代码审计PHP开发框架POP链利用Yii反序列化POP利用链
  • C++单例模式与多例模式
  • 大数据新视界 -- 大数据大厂之 Impala 性能飞跃:动态分区调整的策略与方法(上)(21 / 30)
  • 【go从零单排】JSON序列化和反序列化
  • python功能测试
  • 跟随Facebook的足迹:社交媒体背后的探索之旅
  • vue打包exe之electron-quick-start的npm install 报错
  • 丢失照片/消息/文件,当发现没有备份 Android 手机数据时急救方法
  • Java面试篇基础部分-Semaphore及其用法详解
  • 数据结构-线性表的单链式存储结构图解及C语言实现
  • 都说网络安全缺口那么大,但为何招聘数量却不多?总算明白了!
  • Linux系统部署Mysql8.x修改密码并且设置远程连接
  • UniApp基于xe-upload实现文件上传组件
  • electron的常用弹窗简单案例
  • 15年408-数据结构
  • 老人跌倒扶不扶?涪城三职工给出响亮答案
  • 【docker】在IDEA工具内,远程操作服务器上的docker
  • Rust Web开发常用库
  • Leetcode 706. 设计哈希映射
  • 大屏可视化px转rem方案实现
  • webservice cxf框架 jaxrs jaxws spring整合 接口测试方法 wsdl报文详解 springboot整合 拦截器 复杂参数类型
  • 作者分享|eDNA研究梯级水坝对浮游植物和浮游动物群落变化的影响
  • WPF入门教学十九 属性动画与时间线
  • 计算机网络nat 映射案列