传输层:TCP为什么靠谱

顺序问题和丢包问题

  • 为了保证顺序性,每个包都有一个ID(序号),在建立连接时沟通初始ID,然后按照ID一个一个发送,为了保证不丢包,需要对接收的包有确认,但是这个确认不是收到一个包就确认一个,而是会应答某个ID之前所有的包,这叫累计确认或累计应答
  • 为了记录所有发送的包和接收的包,TCP也需要发送端和接收端分别都有缓存来保存这些记录。
  • 发送端的缓存里是按照包的ID一个一个排列,根据处理情况分成四个部分:
    • 1、发送了且受到确认了,可以从缓存移除。
    • 2、发送了尚未收到确认,需要等待确认,可能需要重发,所以不能直接移除。
    • 3、未发送,等待发送的。
    • 4、未发送,并且暂时还不会发送的。
  • 区分3、4部分是因为有流量控制,在TCP中,接收端会给发送端报一个窗口大小,叫Advertised window,这个窗口的大小应该等于上面的第二部分加上第三部分,超过这个窗口,接收端处理不过来,就不能发送了。
    在这里插入图片描述
  • LastByteAcked是发送已确认和发送未确认的分界线,LastByteSent是已发送和未发送的分界线,LastBytesSent加上AdvertisedWindow大小是可发送和不可发送的分界线。
  • 接收端的缓存里根据处理情况分成三部分:
    • 1、接受过且确认了。
    • 2、等待接收,未确认。
    • 3、不能接收的。
      在这里插入图片描述
  • LastByteRead之后是已经接收,还没被应用层读取的。MaxRcvBuffer是缓存最多放多少,NextByteExpected是已确认和未确认的分界线(未确认分为接收和等待接收,接收的可能不是按顺序的)。
  • AdvertisedWindow=MaxRcvBuffer-((NextByteExpected-1)-LastByteRead),比如图上NextByteExpected=6,那么实际上有6-1=5个包已经接收了(假设序号从1开始),缓存中已经接收了5个,那么剩余空间就是窗口大小。已经被应用层处理的包的序号加上缓存大小就是能接收的最大序号

确认和重发的机制:

  • 方式一,超时重试,对于每一个发送但未收到确认的包,过一段时间就重发,超时时间通过采样往返时间RTT加权平均得到,这个叫自适应重传算法。如果重发第二次又没有收到确认,TCP策略是超时时间加倍,理由是两次超时,说明网络较差,不宜频繁发送。超时重传的问题在于重传周期较长。
  • 方式二,快速重传,当接收方收到一个序号大于下一个所期望的报文段时,就检测到数据流中的间格,发送三个冗余的ACK,比如收到6,8,9,接收方知道7丢了,那么就发送三个(为什么是三个,是一个估计值)6的确认,要求下一个是7,发送方收到这三个确认,就会发现7的确丢了,不等超时,马上重传。
  • 方式三,SACK,这种方式需要在TCP头加一个SACK,可以将缓存地图发给发送方,比如ACK6,SACK8,SACK9。

流量控制

  • 确认包同时会携带一个窗口(滑动窗口rwnd)的大小
  • 如果窗口不变,确认了一个包,窗口右移一格,有一个新包进入可发送状态。
    在这里插入图片描述
  • 如果发送端过猛,发送了窗口大小个包,而这些包都还未得到确认,那此时未发送可发送部分为0。
    在这里插入图片描述
  • 发送方只有在收到一个包的确认,窗口才能右移一个格,才有新包可以发。
    在这里插入图片描述
  • 如果接收方实在处理慢,导致缓存没空间了,可以通过确认信息修改窗口大小(甚至可以设置为0,让发送方停止发送)。假如接收方一直不处理数据,那么收到一个包,窗口就只能减小一。

在这里插入图片描述

  • 发送方收到确认,窗口只有左边界移动了,窗口大小变小了。
  • 在这里插入图片描述
  • 如果接收端一直不处理数据,那么随着确认包越来越多,窗口就越来越小,直到为0。

在这里插入图片描述

  • 当发送端收到最后一个包的确认到达时,发送端的窗口也会调整为0。
    在这里插入图片描述
    当发送端窗口变为0后,会定时发送窗口探测数据包,看是否有机会调整窗口大小。当接收方比较慢时,要防止低能窗口综合征,不要有个空位就告诉发送方,而是要当空位数量达到一定大小,或缓冲区一半为空,才更新窗口

拥塞控制

  • 拥塞控制也是通过窗口大小控制的(拥塞窗口cwnd),拥塞控制的目的是防止网络堵车。
  • 发送速度由滑动窗口和拥塞窗口共同控制:LastByteSent - LastByteAcked <= min {cwnd, rwnd},即发送的数量最大等于两个窗口较小值加上已确认
  • 发送方如何判断网络其实很难,对于TCP协议来讲,网络路径会经历什么压根不知道。TCP发送包就像往水管灌水,而拥塞控制就是在不堵塞不丢包的情况下,尽量发挥带宽
  • 水管粗细就是网络带宽,即每秒能够发送多少数据;水管长度就是端到端的时延,理想状态下,水管里的水量 = 水管粗细 x 水管长度。类比到网络上,通道的容量 = 带宽 * 往返延迟
  • 如果设置发送窗口,使得发送但未确认的包为通道的容量,就能够撑满整个管道。
  • 假设往返需要8s,每秒发一个包,每个包1024byte。过去的8s,则8个包都发出去了,其中前4个包已经到达接收端,但是ACK还没有返回不能算发成功,后4个包还在路上,还未被接收,这个时候整个管道正好撑满。在发送端,这8个包的大小正好等于带宽,即每秒一个包乘以返回时间8秒
    在这里插入图片描述
  • 在此基础上再调大窗口,使得单位时间发送更多的包,会出现什么现象呢:要么中间设备处理不过来,包丢弃;要么包在缓存中排队时间过长,超时重传。拥塞控制就是为了避免这两个现象
  • 一开始如何知道发送速度多块呢?就像倒水,开始慢一点,如果一直能倒进去就加快速度,这叫慢启动
  • 一条TCP连接开始,cwnd设置为一个报文段,一次只发一个;当收到这一个包的确认后,cwnd加1,这次可发两个;再收到这两个确认,每个确认cwnd加1,这次可以发四个;可以看出是以2为指数增长
  • 当超过ssthresh 65535字节(阈值),就要慢点来,因为可能快满了,从此时开始,每收到一个确认,cwnd增加1/cwnd。比如上面一次发送四个,这时一个确认增长1/4,四个确认共增长1;然后一次发五个,五个确认共增长1,变成了线性增长
  • 线性增长到拥塞时,表现为丢包,需要超时重传,这时将ssthresh设为cwnd/2,将cwnd设为1,重新开始慢启动,但这种方式太激进,将一个高速传输速度一下子停下来,会造成网络卡顿
  • 前面讲过快速重传算法,收到三个前一个包的确认,就快速重传当前包,不等待超时。TCP认为这种情况大部分没丢,cwnd减半为cwnd/2,sshthresh = cwnd。当三个包返回的时候,cwnd = sshthresh + 3,接下来依旧是线性增长。
    在这里插入图片描述
  • 其实使用拥塞控制避免丢包和超时重传是有问题的。丢包并不代表通道满了,例如公网上带宽不满也会丢包;此外TCP的拥塞控制要等到将中间设备都填满才发生丢包,但此时已经晚了,只要填满管道就可以了。
  • 为了优化,后来有了TCP BBR拥塞算法,企图找到一个平衡点:通过不断加快发送速度,将管道填满,但不要填满中间设备缓存。
    在这里插入图片描述
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值