TCP协议

特征

面向连接:通讯双发发送数据之前要建立连接

可靠交付:提供尽可靠交付的服务,保证消息不错、不乱、不丢

面向字节流:不缺分消息边界,发送数据为字节流。使用者需要自己区分消息边界。

全双工(full-duplex service):实时的双向通讯。

TCP格式

创建连接

三次握手

 

为什么两次握手不行?

三次握手的本质是通讯双方同步ISN并且相互确认收到对方的ISN,如果只有两次握手,会导致服务器无法确认客户端是否已经收到自己的ISN,因此无论客户端有无收到服务器的ISN,服务器都认为收到,从而认为连接建立完成。这种机制,在某些情况下会导致服务器维持一些无用的连接甚至收到无用的数据,浪费服务器的资源。

情景1:客户端发送的创建连接请求包因网络延迟未及时到达服务器,服务器未对该请求进行ack,导致客户端超时重发该请求。后面重发的请求和前面的请求都到达服务器,会导致服务器维护两条连接,浪费服务器资源。

情景2:客户端发送的创建连接请求包因网络延迟未及时到达服务器,服务器未对该请求进行ack,导致客户端超时重发该请求,服务器对新请求进行确认,之后进行数据交互通讯并断开连接。前面延迟的创建请求网络包到达服务器,后续数据通讯过程中的部分延迟数据也到达服务器,服务器会认为有新连接请求和数据交互,从而维持连接并接收和处理数据,浪费服务器资源。

为什么不要四次甚至更多?

理论上来说,越多的确认可靠性越高。确认、确认的确认、确认的确认的确认。。。,无限确认下去,但是这样做代价太大,对可靠性的提高却没有明显增加。三次是权衡的最佳选择(代价最小,获益最多)。

销毁连接

四次挥手

可靠性机制

不错

检错机制:每个segment都有checksum,通讯双方通过计算checksum来确认消息没有错误。注意:即使checksum计算正确,也不能保证消息是百分百正确的。

不乱

发送方给消息编号(每个消息都有secequence number),接收方按照编号进行顺序拼接后再交给应用。

不丢

由于IP协议不可靠,可能会消息丢失,因此发送方把消息发送后,消息不能立即丢弃,要缓存起来,等到接收方恢复该消息的ack后才能将该消息丢弃。同时,消息发送出去时,需要启动一个定时器,等到一定时间还未收到接收方的ack,那么默认该消息丢失,立即重发该消息。超时定时器解决了消息ack丢失导致的死锁及问题。

效率问题

停等协议导致信道利用率很低,为提高利用率,使用pipeline技术。发送方可以连续不断的发送多个segment给接收方。其实现依赖滑动窗口机制。

发送方

1. 只为滑动窗口中的第一个包启动定时器

2. 接收到ack 则将滑动窗口的base移动到该ack消息位置

3. 连续收到3个同样的ack,则立即发送该ack期望的消息(快重传机制)

接收方

1. 接收到顺序包,则延迟发送ack(启动一个500ms的定时器,如果500ms内有后续的顺序消息到达,则立即发送合并的ack,如果没有,则等500ms时间到,发送ack)

2. 接收到不连续的包,则立即发送最后连续消息的ack

3. 接收到填充不连续区域的包,则立即发送ack

 

发送方超时定时器时间如何确定?

常识:比RTT稍大

实现:

  对segment的RTT进行采样得到sampleRTT 。则 平均rtt(rtt期望):

      EstimatedRTT = (1-a)* EstimatedRTT  + a*sampleRTT     其中 a=0.125

  RTT的方差估算:

      DevRTT = (1-b) * DevRTT  +  b*(sampleRTT  - EstimatedRTT  )

  定时器时间为:

    TimeoutInterval = EstimatedRTT  + 4 * DevRTT 

其数学含义: 根据高斯分布,99.73%的概率分布在3个标准差以内,99.994%的概率分布在4个标准差以内。因此该定时器只有 0.006%的几率超时。

一旦TimeoutInterval 定时器超时触发,则TimeoutInterval 将直接翻倍。避免网络拥塞时频繁超时,导致网络中存在大量重复报文。

流量控制

目的:防止发送方数据发送过快,而接收方来不及接收(接收方处理速度慢),导致大量包被丢弃的问题。

接收窗口反馈机制

 接收方可以通过ack报文中的窗口字段,告知sender其接收窗口大小。sender的发送窗口应该小于接收者的接收窗口。

如果接收者发送的接口窗口为0,意思是告知sender应当暂停发送报文,等到receiver再次发送窗口不为0的报文时,sender再开始发送报文。

极端情况下,receiver发送的窗口不为0的ack可能丢失,这时候sender一直等待receiver的通知却等不到,导致死锁。为了解决这种情况,tcp设置一个持续计时器,当sender收到窗口为0的ack时,就启动该定时器,如果定时器超时还没有收到receiver的ack,就尝试发送一个字节的报文进行探测。

如果sender应用程序产生的数据很慢呢?比如,一秒钟只发送一个字节的数据。如果一次只发送一个字节,那么网络传输效率就会很低。为了避免这种情况,设置了nagle算法。nagle算法规定,如果应用产生数据很慢,就先发送一个自己的数据,其余数据暂存到缓冲区。等到接收到receiver的ack后,再发送缓存的数据。如果缓冲区积累的数据已达到缓冲区的一半或者 达到一个MSS时,也发送数据。

如果receiver消费数据很慢呢?极端情况下,一秒钟只消费一个字节,就会导致tcp每次发送ack只ack一个字节,也会导致sender一个字节一个字节的发送数据,降低传输效率,这种情况称为糊涂窗口综合征(Silly window syndrome),这时候receiver应当等待一段时间,让自己的缓冲区有一半空闲空间或者足够容纳一个MSS时,才发送确认。

拥塞控制

目的:

防止过多的数据注入到网络中,使得网络中的路由器或链路布置过载。拥塞控制是个全局过程。

原因:

网络中的路由器或链路一旦过载,会导致网络包超时重传,使得网络趋于恶化,甚至恶化的趋势比想象中严重,导致死锁。

问题:

  如何判断网络出现拥塞?无法判断

  只能通过网络包是否需要超时重传去推测 网络可能发生了拥塞。

慢启动

  sender维持一个cwnd(congestion window 拥塞窗口 ),让自己的发送窗口不超过这个cwnd。

  窗口大小从1个MSS开始,每收到一个ack, 滑动窗口增加1,这样每经过一个RTT滑动窗口翻一倍。一直增加到ssthreshold,再转入用拥塞避免阶段。

拥塞避免

  进入拥塞避免阶段后,每收到一个ack,滑动窗口增加ssthreshold分之一,也就每经过一个RTT窗口增加1.直到出现超时重传。

  出现超时重传后,ssthreshold降低为原来的一般,然后重新进入慢启动阶段。

由此可以看出,慢启动一点都不慢,可以认为慢启动是指冷启动。

总体上:

  当cwnd<= ssthreshold 时,执行慢启动算法。

  当cwnd> ssthreshold 时,执行拥塞避免算法。

  当出现超时重传时,ssthreshold直接减半。 

ssthreshold的初始值是多少?

AIMD算法:

   multiplicative Decrease  乘法减小,每次出现超时重传,ssthreshold直接降为原来的一半。

   Additive Increase 加法增加:进入拥塞避免阶段,cwnd缓慢增加

快重传

  接收方每次收到失序的报文段,就立即发出重复确认,让sender及时直到有报文段丢失。sender只要收到三个重复确认,就立即重传报文段,而不需要等到超时定时器超时才重传。

快恢复

  sender收到三个重复确认后,ssthreshold减半,不再执行慢开始算法(cwnd从1个MSS开始),而是从新的ssthreshold开始,执行拥塞避免算法。

  为什么这么做?因为sender能够收到3个ack,说明ack都没有丢,网络可能不拥塞(因为拥塞会导致ack也丢失)或者说拥塞的没那么严重,此时为了传输效率,没必要激进的让cwnd从1开始,而是从新的ssthreshold开始。

 

posted @ 2023-09-11 14:23  小张同学哈  阅读(14)  评论(0编辑  收藏  举报