一、TCP协议
TCP协议的首部:
字段解析:
- 源目端口号:各2字节,
short
类型的端口号 - 序号:标识当前TCP数据包在数据流中的序号,即平常的SYN和SEQ信息。
- 确认号:收到数据报后,返回的ACK序号
- 首部长度:TCP协议的首部长度,以四字节为单位,最多60字节。默认不带选项的情况下TCP首部是4个字节。
TCP选项:
URG
: 紧急指针有效,很少使用ACK
: 确认选项PSH
: 推送选项,接收方收到后应该尽快被发送到应用层,选项没有被可靠的实现或用到RST
: 重置连接,经常是因为错误取消- SYN`: 开始连接,初始化一个连接的同步序号
- FIN`: 结束连接
- 窗口大小:2字节,滑动窗口协议中用来控制流量大小的字段
- 校验和:计算方式和UDP一致,也会添加一个伪首部,计算整个头部和数据部分的内容
- 紧急指针:只有在
URG
被设置时才有效,很少用到。 其他选项
MSS
: 最大段大小,表示连接希望收到的报文段的最大值
二、三次握手和四次挥手
三、TCP超时和重传
在连接无法收到响应后,TCP将会尝试重传数据,重传的时间间隔称为二进制指数退避。不过和CSMA/CD协议中的不同,这里的二进制指数退避是在之前的重传间隔上加倍。
0.2 - 0.4 - 0.8 - 1.6 - 3.2 - 6.4 - 12.8 - 25 - 50 - 100 - 200 - ...
linux中可以通过配置来设置重传的次数,重传涉及两个次数:第一个是TCP重新连接时尝试重传的次数,第二个是TCP放弃当前连接的时机。
net.ipv4.tcp_retries1
net.ipv4.tcp_retries2
3.1 RTT
RTT表示一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值。
RTT的取值方式:为一个已发送但尚未确认的报文段做一次RTT采样,得到一个SampleRTT(不是为每一个发送的报文段都测量RTT),从而用这个SampleRTT来接近(代表)所有RTT。
- 一个连接中,有且仅有一个测量定时器被使用。也就是说,如果TCP连续发出3组数据,只有一组数据会被测量。
- TCP决不会为已被重传的报文段测量SampleRTT,仅仅为传输一次的报文段测量SampleRTT。
- ACK数据报不会被测量,原因很简单,没有ACK的ACK回应可以供结束定时器测量。
由于链路负载的变化,很多时候RTT并不能完全代表网络中真正的状态,为了得到一个更合理更接近事实的RTT,TCP规范中通过一个低通过滤器来设置出平滑的RTT估计器:
SRTT = α(SRTT) + (1 - α)RTT
α
一般设置为0.8-0.9,意思是SRTT的值的80%-90%来自前一个估计值,10%-20%来自当前的估计值。
3.2 RTO
RTO指重传超时时间,从数据发送时刻算起,超过这个时间便执行重传。
RTO的值依赖RTT,[RFC0793]
中推荐的RTO计算公式为:
RTO = min(ubound, max(lbound, (RTT)β))
β
是离散因子,推荐值为1.3-2.0,ubound是RTO的上边界(建议为1分钟)lbound是RTO的下边界(如1秒)。这种方法被称为经典方法,它使得RTO值为1秒或者2倍的SRTT。对于相对稳定的RTT来说,这种方法能取得不错的性能。运行在RTT波动较大的网络环境时,无法获得期望的效果。
3.3 TCP中的四个定时器
- 重传定时器:为了控制丢失的报文段或丢弃的报文段,也就是对报文段确认的等待时间。即上面说的RTT和RTO。
- 坚持定时器:为对付零窗口通知而设立的,具体见下面
- 保活计时器:为保持TCP的keepalive设立的,一般为2小时
- 时间等待计时器:关闭连接时需要等待的时间,即2MSL等待期
3.4 坚持定时器
当窗口大小为0时,如果一个确认丢失了,双方就有可能因为等待对方而使连接终止:接收方等待接收数据(因为它已经向发送方通告了一个非0的窗口),而发送方在等待允许它继续发送数据的窗口更新。
为防止这种死锁情况的发生,发送方使用一个坚持定时器(persist timer)来周期性地向接收方查询,以便发现窗口是否已增大。这些从发送方发出的报文段称为窗口探查(window probe)。
3.4 快重传
TCP发送端在观测到多个重复的ACK后,此时重传可能丢失的数据包分组。不要等到计时器超时,此时依旧可以发送新的数据。
四、拥塞避免
4.1 慢启动
一个TCP刚启动时,不知道外部网络环境状态,为了避免发送过多数据出现拥塞,TCP会维护一个从1开始的cwnd
值,每次发送cwnd
个数据包。当收到一个数据报的确认后,该字段值加1。这个过程被称为慢启动。
具体的过程为:
- 连接建好的开始先初始化cwnd = 1,表明可以传一个SMSS大小的数据
- 收到一个ACK,cwnd++,呈线性上升
- 过了一个RTT,cwnd = cwnd*2,呈指数上升
- 每当产生一个RTO超时重传,cwnd = 1, ssthresh减半
慢启动有一个门限值ssthresh
,超过这个值之后会启动拥塞避免算法。
4.2 拥塞避免
当cwnd
超过ssthresh
时,开始采用拥塞避免算法。因为cwnd
不可能无限增加,拥塞避免算法的目的是使得cwnd
处于更缓慢增长的模式。
假设cwnd=k*SMSS,当发送端收到一个包的ACK反馈的时候,按照cwnd=cwnd+(1/k)*SMSS。这样每经过一个RTT发送k个数据包的时候,cwnd增长了大约一个SMSS,通常上我们即描述为每经过一个往返时间 RTT 就把发送方的拥塞窗口 cwnd 加 1,使拥塞窗口 cwnd 按接近线性规律缓慢增长。这种增长方式叫做“加法增大”(Additive Increase)。总结如下:
- 收到一个ACK时,cwnd = cwnd + 1/cwnd
- 每过一个RTT时,cwnd = cwnd + 1
使用慢启动和拥塞避免算法产生的流量增长趋势图:
此处评论已关闭