|
84 | 84 | 2. TCP的一条连接由一个四元组(src_ip, src port, dst_ip, dst_port)标识,当处于`TIME_WAIT`时,此条连接会被定义为不可使用,这样就避免了一条连接关闭后能立即生成同一条连接,接收到上一次连接的分组。 |
85 | 85 |
|
86 | 86 | **避免TIME_WAIT** |
87 | | -`SO_REUSEADDR`这个套接字选项允许分配处于`TIME_WAIT`的端口来建立TCP连接。这一点违背了TCP的最初规范,但是只要能保证不会收到上一条连接分组的干扰,就可以使用。 |
| 87 | +`SO_REUSEADDR`这个套接字选项允许分配处于`TIME_WAIT`的端口来建立TCP连接。这一点违背了TCP的最初规范,但是只要能保证不会收到上一条连接分组的干扰,就可以使用。 |
| 88 | + |
| 89 | +## 超时重传 |
| 90 | +TCP超时重传的基础就是需要根据RTT(Round-Trip Time)分组往返时间设置RTO(Retransmission Timeout)超时重传时间。 |
| 91 | +上述过程有以下方法: |
| 92 | +* 经典方法 |
| 93 | + SRTT = a(SRTT) + (1-a)RTT,其中a取0.8-0.9,SRTT为平滑RTT估计值,RTT为新测的一个RTT样本。得到新的SRTT后,RTO = min(ubound, max(lbound, (SRTT)b)),其中ubound是RTO的上边界,lbound为RTO下边界,b为时延离散因子,通常为1.3-2.0。 |
| 94 | + 此方法在RTT波动较大的网络中,效果很差。 |
| 95 | +* 标准方法 |
| 96 | + 基于平滑RTT和平滑偏差计算 |
| 97 | + (平滑RTT)srtt = (1-g)srtt + gM |
| 98 | + (平滑偏差)rttvar = (1-h)rttvar + h(|M-srttt|) |
| 99 | + RTO = srtt + 4rttvar |
| 100 | + |
| 101 | +## 快速重传 |
| 102 | +根据收到冗余的ACK来判断是否发生丢包,通常冗余ACK的数量有个阈值dupthresh(常量3),到这个值时,大概率产生丢包,发生重传。 |
| 103 | + |
| 104 | +## 重传二义性和karn算法 |
| 105 | +* 当重传了一个数据包后,收到一个确认,该确认可能是第一次传输的确认也可能是重传的确认。 |
| 106 | +* 很明显根据此确认计算RTT可能不是准确的,所以karn算法第一部分规定,接收重传的确认时,不更新RTT估计值。 |
| 107 | +* 每重传一次,计算RTO的退避系数加倍,这是karn算法的第二部分。 |
| 108 | + |
| 109 | +# 数据流与窗口管理 |
| 110 | + |
| 111 | +## 延时确认 |
| 112 | +大多数情况下,TCP不会为每个受到的数据包返回一个ACK,而是累计收到多个后,返回一次ACK,但是为了避免引发重传,延时理论值最高为500ms,实际中最大取的是200ms。 |
| 113 | + |
| 114 | +## Nagle算法 |
| 115 | +为了减少网络中微型包的数量,引入了nagle算法。 |
| 116 | +* 一个TCP连接中只允许有一个小于MSS的报文段在传输,在该报文段的ACK到达之后,TCP会收集小数据并整合为在一个报文段中再发送。 |
| 117 | + |
| 118 | +### 延时ACK和Nagle算法 |
| 119 | +若两者结合使用,结果可能会很不理想。比如说客户端使用延时ACK,服务端使用nagle算法,当服务端发送了一个小包后,会一直等这个包的ACK,而客户端接收到这个小包后进入延时ACK的计时器,希望有新的数据包捎带此ACK回复给服务端,这样两者会互相等待对方,造成暂时性死锁。 |
| 120 | +**禁用nagle**:设置TCP_NODELAY选项。 |
| 121 | + |
| 122 | +## 滑动窗口和接收窗口 |
| 123 | +### 滑动窗口 |
| 124 | + |
| 125 | +1. 关闭:窗口左边界右移。当已发送的数据收到ACK时,窗口会减小。 |
| 126 | +2. 打开:窗口右边界右移。当已确认数据的到处理,窗口会变大。 |
| 127 | +3. 收缩:窗口右边界左移。 |
| 128 | + |
| 129 | +### 接收窗口 |
| 130 | + |
| 131 | +到达的序列号小于RCV.NXT或大于RCV.NXT+RCN.WND则丢弃。只有序列号等于RCV.NXT时,窗口才会向右移动,但是设置了SACK选项后,也可以确认窗口内的其他报文段(乱序包),直到收到序号等于RCV.NXT的包,窗口才会向右滑动。 |
| 132 | + |
| 133 | +### 零窗口和持续计时器 |
| 134 | +当接收端的窗口值变为0时,会通知给发送端,直到应用程序处理数据,窗口重新获得可用空间时,会给发送端发一个窗口更新(一个纯ACK),不保证传输可靠,所以如果丢失,双方都会等待,发送方等待窗口更新,接收方等待接收数据。为了避免此死锁发生,TCP引入了 **持续计时器**。 |
| 135 | +* 当发送端收到零窗口通告时,会开启持续计数器,间歇性的发送窗口探测,强制要求接收端回复一个ACK(包含了窗口大小)查询接收端的窗口是否增长。一般在第一个RTO后发送第一个窗口探测,随后采用指数规避发送窗口探测。 |
| 136 | + |
| 137 | +### 糊涂窗口综合征(silly window syndrome) |
| 138 | +发生该问题的时候,TCP交换的不是全长包,而是一些小包,小包传输代价相对大,传输效率很低。 |
| 139 | +出现原因: |
| 140 | +* 接收端通告窗口太小 |
| 141 | +* 发送端发送的数据较小 |
| 142 | + |
| 143 | +应对: |
| 144 | +* 接收端:窗口增至可以放下一个全长报文段,或者接收端缓存的一半(两者中较小者)的时候才能开始发送新窗口通告。 |
| 145 | +* 发送端:1.可以发送一个全长报文段 2.可以发送数据段长度 >= 接收端通告过的最大窗口值的一半的报文段 3.当禁用了Nagle算法或者所有发出的数据都已经被对端ACK确认的话,那么TCP可以发送小数据包 |
0 commit comments