tcp中的一些细节

RTT(往返时延)

RTT的估计和超时

tcp像很多其他的协议一样,超时的时候会重发,但是如何估计往返时间和设定超时时间是一个需要考虑的问题

估计往返时延

segment里面有SampleRTT,计算方式是一个segment发出的时间,到ack收到的时间,对于重发的segment则不进行计算

显然这个值会因为网络状况产生一些波动,为了估计一个典型的RTT. TCP维护一个平均值,EstimatedRTT:

$$
\mathtt{EstimatedRTT}=(1-\alpha)·\mathtt{EstimatedRTT}+\alpha·\mathtt{SampleRTT}
$$

其中常用的$\alpha$值为$0.125$

DevRTT

DevRTT是用来衡量SampleRTT波动程度的值,公式如下:

$$
\mathtt{DevRTT}=(1-\beta)·\mathtt{DevRTT}+\beta·\mid\mathtt{SampleRTT}-\mathtt{EstimatedRTT}\mid
$$

EstimatedRTT和DevRTT都是一个EWMA(指数加权移动平均值)

计算重发间隔

有了EstimateRTT和DevRTT,超时时间应当如何计算?首先它需要不小于EstimateRTT,但是又不能太大,防止延迟过高。
所以设置方式通常是在EstimateRTT上加一个margin,这个margin的值需要根据SampleRTT的波动情况来设置。因此TCP采用的方式是:

$$
\mathtt{TimeoutInterval}=\mathtt{EstimatedRTT}+4·\mathtt{DevRTT}
$$

推荐使用的初始值则是1秒,当发生超时时,TimeoutInterval会倍增,直到它被重新计算。

可靠的数据传输

TCP在IP协议的基础上提供了可靠的数据传输,解决了数据错误,丢包,重复和顺序错乱的问题。

TCP推荐只维护一个重发的计时器,尽管可能有很多还没有收到ACK的包

一个简化的模型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
NextSeqNum=InitialSeqNumber
SendBase=InitialSeqNumber

loop (forever) {
switch(event)

event: data received from application above
create TCP segment with sequence number NextSeqNum
if (timer currently not running)
start timer
pass segment to IP
NextSeqNum=NextSeqNum+length(data)
break;

event: timer timeout
retransmit not-yet-acknowledged segment with
smallest sequence number
start timer
break;

event: ACK received, with ACK field value of y
if (y > SendBase) {
SendBase = y
if (there are currently any not-yet-acknowledged segments) {
start timer
}
}
break;

}

有了上述的简化模型我们就可以进行一些场景模拟了

场景一

  1. Host A向Host B发了一个包,seq=92, length=8 bytes,
  2. Host B向Host A发了一个ack=100,但是丢了
  3. timer时间到了,Host A重发了这个包
  4. Host B发现是收过的,直接给Host A发了ack=100
  5. Host A收到,继续正常工作

场景二

  1. Host A向Host B发了一个包,seq=92,length=8 bytes
  2. Host A向Host B发了一个包, seq=100,length=20 bytes
  3. Host B收到seq=92的包,发了一个ack=100
  4. Host B收到seq=100的包,发了一个ack=120
  5. 两个ack包没能在超时前收到,Host A重发了seq=92的包
  6. Host B收到seq=92的包,发了一个ack=120
  7. Host A开始发seq=120的包,继续正常工作

场景三

  1. Host A向Host B发了上述1.2.中的两个包
  2. Host B发了一个ack=100,丢了
  3. Host B发了一个ack=120
  4. Host A在超时前收到了sck=120
  5. Host A发送seq=120,继续正常工作

添加翻倍机制

在上述的简化模型中,加入如下机制:

每当timeout重发时,将TimeoutInterval翻倍,可以在网络拥塞时产生一些效果

在CSMA/CD中你会看到类似的做法。

快速重传

在简单的模型上进行修改,已达到如下效果:

  1. Host A向Host B连发了5个包
  2. Host B收到了第一个包,并发送了ack=100
  3. 第二个包丢了
  4. Host B在收到3,4,5时连发了三个ack=100
  5. Host A的timer还没有超时,但收到三个duplicated ack了
  6. Host A重发seq=100
1
2
3
4
5
6
7
8
9
10
11
12
13
event: ACK received, with ACK field value of y
if (y > SendBase) {
SendBase=y
if (there are currently any not yet acknowledged segments) {
start timer
}
} else {
increment number of duplicate ACKs received for y
if (number of duplicate ACKs received for y==3) {
resend segment with sequence number y
}
}
break;

流控制(暂时略过)

TCP连接管理

这部分关注的是TCP连接的建立和断开

三次握手

Step1

客户端向服务端发起请求,带有SYN,并且随机选择一个Seq值x,把报文段发到服务端

Step2

服务端收到报文段,分配TCP连接的缓冲区,初始化一些变量。
服务端发送一个SYN报文给客户端,ack=x+1,seq为随机值y
此报文称为SYNACK报文

Step3

客户端收到SYNACK,分配缓冲区,初始化一些变量。
向服务端发送ack=y+1,seq=x+1

四次挥手

Step1

客户端向服务端发送FIN报文

Step2

服务端向客户端发送ACK报文

Step3

服务端向客户端发送FIN报文

Step4

客户端向服务端发送ACK报文。
服务端收到ACK后将关闭,客户端等待一段时间后关闭

TCP的生命周期

客户端