TCP协议的两个原则

两大原则———可靠传输和提高效率

可靠性:

  • 校验和
  • 序列号
  • 确认应答
  • 超时重传
  • 连接管理
  • 流量控制
  • 拥塞控制

提高性能:

  • 滑动窗口
  • 快速重传
  • 延迟应答
  • 捎带应答

确认应答(ACK)机制——可靠传输的核心
在TCP中,当发送端的数据到达接收主机时,接收端主机会立刻返回一个已收到消息的通知。

TCP协议把每个字节的数据都进行了编号,即为序列号。
确认序号是指当前确认序号之前的所有数据都已经收到了,再次发送应该从确认序号开始发送。
这里写图片描述
除SYN,FIN外,发送普通数据也是通过ACK来确认的

超时重传——确认应答的补充
- 数据丢失
这里写图片描述

  • ACK丢失
    这里写图片描述

TCP拥有自己的一套去重机制(通过识别序列号)

♥♥ 超时重传的时间该如何确定?
找一个最小的时间,保证确认应答一定能够在这个时间内返回
这个时间不好确定,因为网络环境的不同,时间会有差异。若时间设的太长,会影响重传效率,太短又会频繁发送重复的包。
为了性能,TCP会动态计算这个时间。
BSD的UNIX和Windows系统中,超时以0.5秒为单位来控制。
刚开始的超时时间一般设为6秒左右,若还未收到ACK,就重发;之后超时时间会以2倍,4倍的指数函数延长。达到一定重发次数后还未收到ACK,就认为对端异常,强制关闭连接。

滑动窗口——提高效率的重要保证

每发送一个数据,就等一个ACK,包的往返时间越长,通信性能越低,效率越差。
这里写图片描述

效率和可靠性会产生矛盾
这样一发一收效率太低,我们可以批量发送,批量等待,这就是窗口
这里写图片描述
窗口大小:是指不用等待确认应答就可继续发送数据的最大值(上图中为4000)
滑动:当等到第一条ACK回来时,窗口向后划一格,窗口中保存的是数据已经发送出去了,但ACK还没到的数据
这里写图片描述
为了维护滑动窗口,操作系统内核会开辟发送缓冲区来保存未应答的数据,只有确认应答过的数据才能从缓冲区删除。

窗口大小不能设置太大,受限于接收端的接收能力和网络的拥堵程度

★★ 使用窗口控制,若出现丢包,该如何重传?
这里我们分两种情况来讨论:
- 数据抵达,ACK丢失
这种情况没关系,因为ACK会相互补充。并且TCP为提高效率,可能会少发一部分ACK。
这里写图片描述
- 数据包丢失
这里写图片描述
上图中1001~2000数据包丢失,B主机就会一直向A发送1001的ACK,当A收到三次1001的ACK,就明白了B的意思,即会重传1001~2000数据,B一旦收到重传数据,再返回的ACK就是7001了。因为接收端缓冲区中已经放好了7001以前的数据。
这种机制就称为“快重传机制”

快速重传——超时重传和滑动窗口两者结合的产物

没有多余的操作,谁丢了就重传谁
TCP接收端有一个接收缓冲区将收到的数据保存下来。去重和重传都依赖这个接收缓冲区。
也有一个发送缓冲区,重传机制依赖此,当确认对端收到这个数据,才会从发送缓冲区中删除该数据,若未收到确认,应在缓冲区中保存一段时间。

流量控制——流量控制窗口

根据接收端的处理能力,来决定发送端的发送速度,这就叫做流量控制。
接收方根据缓冲区空闲大小来反应接收端的接收能力(类比于水库的水位高低),建议发送方发送速率应该是多少
接收端会将自己接收缓冲区大小放在TCP首部中的”窗口大小“字段中,通过ACK来告诉发送方。
发送方根据接收方的窗口大小来调整自己的发送速度。
当接收缓冲区满时,发送方不会再发送数据,但会时不时发送一个窗口探测报文,一旦发现接收方缓冲区不为0,就向接收方继续发送数据。

这里写图片描述

TCP首部中的窗口大小是16位,最大表示65535,TCP实际窗口大小很多时候可能不止这么一点,TCP首部的选项中还包含了一个窗口扩大因子M,实际窗口大小是窗口字段的值左移M位。

★ 生产者消费者模型
当我们在程序中调用read即是消费缓冲区中的数据,这时缓冲区就有空闲位置。

拥塞控制——拥塞窗口

根据网络的拥堵程度决定发送方的发送速率
采用“慢开始”机制先探测网络状态,再决定发送的速率

这里写图片描述

这样下来,拥塞窗口的增长速度是指数级别的,为了使其慢下来,这里引入了一个“慢启动阈值”,一旦拥塞窗口的值大于阈值,就使其线性增长,当网络发生阻塞时,慢开始阈值就变为此时拥塞窗口的一半,同时拥塞窗口置为一。

这里写图片描述

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

滑动窗口:在不等待ACK到达的前提下,发送方批量发送的数据是多少
流量控制窗口:接收方缓冲区的剩余空间
拥塞控制窗口:发送方按照一定的规则来试探网络的拥堵程度
实际发送用的窗口(滑动窗口)就取二者中的最小值
后二者中较小的来决定真实的发送速率

延迟应答——使滑动窗口尽可能大
给接收端留一点时间来处理缓冲区的数据
窗口越大,发送效率就越高,网络利用率也就越高。

不是所有的包都可以延迟应答,一般是每隔N个包应答一次或是超过最大延迟时间就应答一次。
TCP采用滑动窗口机制,因此即使确认应答少一些也不影响。TCP文件传输中 ,大多是每两个包应答一次。
这里写图片描述

捎带应答:基于延迟应答,两个应答合并成一条(应用层的应答数据和上一条请求的ACK数据)
这样,就会提高网络利用率,也可以减轻计算机负荷。确认应答必须等应用处理完数据并返回时,才能进行捎带应答。
若未启用延时应答,也就无法实现捎带应答

基于上述种种机制,TCP提供了可靠、高速的服务。

在应用程序中当我们创建一个socket时,内核会创建一个发送缓冲区和接收缓冲区。
当调用write时,数据会先写入发送缓冲区中,当数据很长,会分成多个TCP数据包进行发送,若数据很短,就先放在缓冲区中,等待数据长度足够或人为刷新缓冲区等操作才会发送。发过去的数据也是先放在接收缓冲区中,当调用read时,才会从缓冲区中拿走数据。
TCP的一个连接两端既能发数据,也能接收数据,也就实现了全双工。
因为有了缓冲区,所以是面向字节流的。

粘包问题
TCP采用了很多机制和优化方法(Nagle)来提高传输效率,这样就会将多次间隔小、数据量小的数据合并成一个大的数据块进行封包发送,而且TCP是面向字节流的,由于应用层传过来的数据包是一连串的字节数据,接受方应用程序不知道从哪到哪是完整的一部分数据,导致读出的数据不是一个完整的应用层数据包,这就是”粘包问题“。

如何解决?
明确两个包之间的界限。

  • 对于定长数据包,我们根据他的长度来读取(如一个结构体)
  • 对变长包,可以在包头部约定一个包长度字段
  • 在包与包之间使用明确的分隔符(应用层协议,自己手动定制)

UDP协议存在粘包问题吗?
UDP连接中接收端缓冲区采用链式结构来记录每个到达的UDP包,并且UDP是面向数据报的,每次要么收到完整的UDP报文,要么不收,因此不会出现粘包问题。

TCP提高可靠传输,一般用于文件传输等重要场景
UDP一般用于对实时性要求较高,但对安全性无特殊要求的场景,如QQ,视频等。

★★★ UDP如何实现可靠传输?

UDP的可靠性需要由上层应用实现,根据TCP可靠传输的机制:
- 引入序列号,保证数据顺序
- 引入确认应答,确保对端收到数据
- 引入超时重传,丢包时重新发送
- 引入滑动窗口,加快传输速率
- 引入流量控制,防止数据溢出
- 引入拥塞控制,避免大量丢包
- ……

猜你喜欢

转载自blog.csdn.net/shidantong/article/details/81166179