在传输层处理的是一个个的TCP段Segment。
TCP段里的序列号和ACK号不是段的编号,而是利用数据的字节数来计数得到的。
序列号:
ACKs
Q:接收方如何处理乱序到达的Segment?
这里给出一个telnet
远程登陆的应用例子:
上图中,在发送数据前,主机A和B已经建立连接并交换过信息,因此,这里初始的Seq=42和Ack=79都是之前建立连接时随机选取的初始序号。
问题:如何设置定时器的超时时间?
问题:如何估计RTT的时间
采用指数加权移动平均的方法,使用新的SampleRTT来更新当前的估计值,即考虑了历史结果,也考虑了最新的测量数值。
确认了RTT的估计值后,需要依据RTT来设置对应的定时器
定时器超时时间的设置:
因为实际需要设置的超时时间是要比EsitimatedRTT大,因此还需要加上一个边界,这个边界值的设置需要与网络当前的状况相关联。如果网络传输状态较好,则可以设置一个较小的边界,否则需要设置一个较大的边界。
测量RTT的变化值:SampleRTT与EstimatedRTT的差值
类似于做方差,得到的差值DevRTT一定程度上表示网络的拥塞状况。
思考:为什么是4倍的DevRTT?这样的设置是否合理?
从应用层接收数据
超时
收到ACK
TCP发送端程序伪代码如下所示:
首先初始化一个SendBase和NextSeqNum变量,用于标识发送端滑动窗口的相关参数。接下来是一个无限循环的过程,用switch方法区分不同的event事件。
左图表示了一个ACK丢失的场景。主机A向主机B发送一个8字节大小的数据,序列号为92。主机B向主机A返回一个ACK=100,因为上一个请求的序列号为92,而发送数据大小为为8字节,因此接收方期望的发送方下一个Segment的Seq应该为100。
但是这个返回的ACK丢失了,主机A发送timeout时间,因此又重传了刚刚的8字节数据。
第二次发送方收到了这个ACK=100,因此将SendBase更新为100。下一次再发送Segment则使用100这个序号。‘
右图表示了timeout时间设置过短的场景。主机A基于流水线机制,连续发送了两个Segment,一个为8字节,一个为20字节。主机B两个Segment都收到了,分别返回ACK=100,ACK=120。但是发送端的超时时间设置多短,导致还未收到ACK=100时,直接重传了第一个Seq=92的数据,然后才陆续收到这两个返回的ACK,并根据ACK序号更新SendBase。而之后,接收方又收到了重传的Seq=92的Segment,由于TCP采用的是累计确认机制,因此即使接收方收到Seq=92,仍然返回的ACK=120。因此在累计确认机制下,每个ACK的的确认序号都表示当前序号前的Segment已经被确认了。
再来看下另一个例子
这次主机A在发送Seq=92和Seq=100的两个序号后,第一个ACK=100的确认丢失了,但是第二个ACK=120的确认正确到达了发送方。还是由于采用的是累计确认机制,发送端在ACK=120后,有理由相信序号120前的Segment都已经被接收方正确接收了,因此直接更新SendBase = 120,而不关心ACK=100丢失的问题。
TCP接收方,重点是ACK的生成原理和对于场景
左边表示接收方的事件,右边表示该事件触发的接收方操作。
TCP实现中,如果发生超时,超时时间间隔将重新设置,即将超时时间间隔加倍,导致其很大。
因此,需要设置额外的重传机制来应对这种情况
通过重复ACK检测分组丢失
因此TCP采用的是流水线和累计确认机制,通过上面的分析我们已经直到,如果接收方收到了乱序的Segment,发送的ACK仍然是那个期望序列号的ACK,也就是期望按序到达的Segment的序号。因此,在流水线机制下,如果有一个Segment丢失了,那么其他的Segment到达后,接收方发送的ACK序号都是那个丢失Segment的序号。因此如果发送方检测到多个某个序号的ACK,就可能说明这个Segment丢失了。
如果Sender收到对同一数据的3个ACK,则假定该数据的段已经丢失
快速重传算法伪代码:
思考:为什么是3次ACK进行快速重传?
接收方为TCP连接分配buffer
左边是有数据从IP层发送过来,然后将数据放到RcvBuffer缓存里,最后向上交付到应用层。蓝色部分表示目前接收端可以用来接收buffer部分。
此时,如果上层应用处理buffer中数据的速度较慢,而发送方发送的数据速率过快,就会导致接收方没有那么多的缓存大小来接收数据,导致buffer缓存溢出。
因此,流量控制的目的就是让发送方不会传输的太多、太快以至于淹没接收方(buffer溢出)。
本质上,流量控制是一场速度匹配机制。
由上图可知,Buffer中的可用空间(spare room)
= RcvWindow
= RcvBuffer - [LastByteRcvd - LastByteRead]
Receiver通过在Segment的头部字段(TCP段的Receive Window字段)将RcvWindow告诉Sender,Sender限制自己已经发送的但还未收到ACK的数据不超过接收方的空闲RcvWindow尺寸。
假设接收方Buffer已经满了,Receiver告知Sender RcvWindow=0,会出现什么情况?
当RcvWindow=0时,即接收方缓存满了,要求发送方不能发送数据了。但是这种情况下,发送方和接收方就无法交互了,也就是说,当后续接收方即使有了空余的buffer,也无法再发送给接收端告知它继续发送数据了。
因此,对于这种情况,需要一些额外的处理。即使RcvWindow=0,在TCP中,发送端仍然可以发送一个很小的一个段Segment,从而带回来接收端的最新buffer大小,以此来解决上述问题。
TCP是一个面向连接的协议,在传输实际数据前,需要首先建立连接。除了建立连接外,TCP还需要初始化TCP变量,比如SeqNumber、Buffer和流量控制信息等。数据传输完后,还需要处理连接的拆除。
TCP在建立连接的过程中,采用的是三次握手的机制。
思考,为什么建立连接是3次握手?2次行不行?经典的TCP建立连接问题
TCP连接建立示意图如下所示:
TCP连接管理:关闭
TCP连接的关闭,从服务端或客户端发起都可以,一般主要从客户端发起。
TCP客户端生命周期
TCP服务端生命周期