TCP的可靠性是怎么保证的?

TCP通过三次握手和四次挥手之后进行数据传输,它们是建立在序列号机制和确认应答机制的基础之上,如果保证这个机制的可靠性还需要一些其他辅助,TCP的可靠性保证包括:重传机制,滑动窗口,流量控制,拥塞控制等。

重传机制        

tcp的可靠性依赖于序列号机制和确认应答机制,即一端发送数据给另一端,另一端都会回复ack包,这样才保证这条数据发送成功,而在这个过程中会有两种可能发生,一种是数据包未到达接收端,原因是数据丢失或者延时了;一种是ack包未到达发送端,原因也是丢失或延时了。前者数据未到达接收端,后者数据已经到达接收端,只是回复的ack包丢失了,未到达发送端。

tcp采用重传机制解决丢包和重复发送问题,tcp中重传包括超时重传,快速重传,sack和d-sack。

超时重传

顾名思义就是超过一定时间未收到回复就重新发送数据,这里比较难以确定是超时重传的时间RTO,这个时间太大和太小都不合适,应该是比数据包一个来回的时间RTT多一点才合理,但是数据包一个来回的时间RTT不是固定的,会受到网络波动的影响,所以RTT的时间是按照几次来回时间进行加权平均值和RTT的波动范围计算出来的,而RTO是在此基础上通过一些系数换算出来的。

快速重传

虽然有超时重传,但是有些数据包等到真的超时再重传就有些太慢了,因此linux还有一种重传机制叫做快速重传。原理是:当发送方发送一条数据seq2后,未能得到ack数据包返回,此时如果后面又连续发送了几条数据seq3,seq4,seq5,seq6,而后面这几次收到的ack数据包都是ack2,ack2,ack2,ack2,意思是接收端已经收到了seq2之前的数据,但是seq2还没有收到,快速重传机制就是在发送端如果发送了多条数据,但是每个数据包的回复包的序列号都是相同的,比如这个例子中seq3,seq4,seq5,seq6返回的数据包都是ack2,这个2指的是序列号,表示接收端缺失的最大的数据的序列号是2,发送端应该发送seq2过来,如果有连续的三个ack2,tcp就会判断需要重发seq2,这种情况可以解决超时重传等待时间过长的问题,但是新的问题是发送端不知道重新发送seq2还是重新发送seq3,seq4,seq5,seq6。

sack可以解决这个问题,引入sack,这个字段的值会放在tcp头的选项字段上,就是发生上面例子中的情况下,后面接收端每次收到请求都会回复一个ack和sack,这两个值中间的部分就是当前接收端缺失的数据,即ack<=x<sack。x就是缺失的那部分数据,这部分数据的序列号起始是ack,末端是sack-1,这样发送端就能知道接收端缺失的是哪部分数据,发送端只需要重新发送这些数据就可以了。

还有一种情况就是发送端发送的数据在网络中延时了,并没有丢失,那么在发送端进行重传后,这个延时的数据又到达了,这样就造成重复发送,这种情况下会采用d-sack方式,dsack其实就是利用sack处理重复数据的一种方式。依然是接收端回复ack+sack,只不过sack表示当前重复的这条数据的序列号,ack表示需要接受的序列号,这样发送端就能知道这条数据已经发送过了。

滑动窗口

TCP为保证可靠性使用确认应答机制,就是说发送端发送一条数据到接收端,接收端收到后回复一个应答数据,一次对话才算结束,然后发送端才会发送下一条数据。

这样的方式无效率很低,所以为了实现可靠以及高效,TCP引入滑动窗口和流量控制。

TCP推出滑动窗口的概念,滑动窗口就是接收端和发送端为每个socket开辟一块空间,只有在接收端滑动窗口空闲的时候才能处理发送端的数据。

原理:发送端每次发送数据到接收端,接收端都会返回一个滑动窗口大小,表示接收端能接受的最大字节数,发送方接收到这个滑动窗口大小后可以连续发送多个数据包,只要在滑动窗口范围内即可。

发送方的滑动窗口有如下区域:

  • 已发送还没有收到回复区域

  • 未发送区域

当已发送的数据得到回复后,滑动窗口右移。

接收端的滑动窗口有如下区域:

  • 已接收但是还没有被应用取走

  • 还未接收的区域

如果接收的数据被应用取走,窗口右移。

在滑动窗口范围内发送端连续发送的几个数据包,如果中间有一个包的ack丢失了,不一定需要重新发送,发送端可以通过下一个ack确定丢失的这个ack包需要不需要重发。也就是说有了滑动窗口的概念,当ack回复包丢失后不一定需要重发。发送端的窗口大小是由接收端决定的。

流量控制

滑动窗口的概念的提出,使得一次可以发送多个数据包,解决了一次只能处理一个数据包,效率低的问题。但是因为接收端的处理能力是有限的,作为发送端不能源源不断的给接收端发送数据,如果数据流量大了,接收端处理不了,就只能丢弃数据了,所以必须有一种机制可以控制数据的流量。

TCP的流量控制也恰恰是基于滑动窗口的,滑动窗口由接收端确认后发送给发送端,发送端根据窗口大小进行发送数据,就能保证发送的数据在接收端都能被接收处理。

但是基于滑动窗口实现流量控制TCP考虑了这样几个问题:

滑动窗口是socket缓冲区中的一块空间,socket对应的缓冲区也不是一成不变的,所以如果缓冲区变化对滑动窗口的同步就会造成一些影响。比如接收端通知发送端窗口大小为100,但是此时操作系统把socket缓冲区减小了到了50,收到的数据大于50,就会把包丢掉就出现了丢包现象。

还有一个问题,糊涂窗口问题,比如因为接收端处理比较慢,滑动窗口为0,窗口处于关闭状态,一段时间后,窗口出现了可能50个字节空闲空间,这时候就会把窗口=50通知发送端,发送端接收到窗口=50后,就会发送50字节的数据过来,但是要知道不管发送多少数据,tcp都要给数据包上tcp头,tcp头就有20字节,同时还要包ip头,也是20字节,足足40字节,而发送的数据就只有10字节,性价比是极低的。而且还会占用带宽。

tcp不允许同时减少缓冲区大小和窗口大小,如果需要减少缓冲区大小,必须先减少窗口大小,一段时间后再减少缓冲区大小。

tcp规定接收端在窗口大小<min(mss,缓冲区的一半)的时候就要关闭窗口(回复接收端窗口为0),这样就能保证接收端不会发送小的窗口给发送端了。

发送端也要解决发送小数据的问题,发送端是通过nagle算法进行延迟处理,即满足以下两个条件中的一个才可以发送:

  1. 窗口大小大于mss或者数据大小大于mss

  2. 收到上一个数据发送回复的ack

只要满足这两个中一个就可以发送。但是这个算法一旦开启就会造成一些数据本身就很小的包不能及时发送,所以这个算法开启要慎重考虑。

tcp依靠上面的机制实现流量控制,具体的流程就是接收端每次都会给发送端回复窗口大小,当接收端很忙的时候,可能窗口就会变小到0,就会通知发送端窗口关闭,此时发送端就不会再发送数据给接收端,当接收端窗口变大后,就会主动回复发送端窗口大小。但是这个回复可能会丢失,那么这是就会出现互相等待的问题,类似于死锁,tcp的解决方案是定义一个时钟,就是一个定时器,当到达一定时间接收端还没有通知窗口的话,发送端就会发送探测报文,一般每30-60秒发送一次,发送三次(这里不同实现可能不一样,可以配置),如果回复了窗口就开始发送数据,如果回复的窗口依然是0就重置时钟重新计时,如果最终都没有打开窗口,发送端可能会发送rst包给服务端终止连接。

拥塞控制

滑动窗口和流量控制保证了接收端繁忙的时候,数据不会因为无处安放而丢弃。

但是网络是共享的,网络也会存在很繁忙的情况,如果网络拥堵,也会造成丢包,tcp在没有收到ack的时候就会重传,使得网络更加拥堵,丢失数据会更多,就会使得整个网络环境更差。

tcp为解决网络拥堵带来的数据丢失问题,提出了拥塞控制。

在拥塞控制机制中的两个概念:拥塞窗口和拥塞算法。

tcp认为只要是出现超时重传就算拥堵了。tcp在发送数据的时候,发送数据的大小受到滑动窗口和拥塞窗口的限制,也就是发送端能发送的最大数据量是拥塞窗口和滑动窗口的的最小值。

  • 19
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值