TCP超时重传是保证TCP可靠性传输的机制之一,当超时后仍没有收到应答报文,就重传数据包并设置超时时钟(超时时间一般增大到原超时时间2倍);直到收到应答报文或超过最大重试次数。
linux TCP超时重传是通过设置重传超时时钟icsk_retransmit_timer来实现的。
零窗探测超时时钟与重传超时时钟共用icsk_retransmit_timer,根据icsk_pending是ICSK_TIME_RETRANS、ICSK_TIME_PROBE0来判断是重传超时还是零窗探测超时。
只有当发送方被通告零窗,连接双方没有数据来往而使接收方无法通过ACK报文通告新窗口时,才使用零窗探测机制;所以重传队列中有重传包时,不会出现零窗探测,出现零窗时不能再发送新数据也就没有重传;所以零窗探测超时时钟与重传超时时钟可以共用icsk_retransmit_timer
I.超时处理函数
i.tcp_retransmit_timer
281 /*
282 * The TCP retransmit timer.
283 */
284
285 void tcp_retransmit_timer(struct sock *sk)
286 {
287 struct tcp_sock *tp = tcp_sk(sk);
288 struct inet_connection_sock *icsk = inet_csk(sk);
289
290 if (!tp->packets_out)
291 goto out;
292
293 WARN_ON(tcp_write_queue_empty(sk));
294
295 if (!tp->snd_wnd && !sock_flag(sk, SOCK_DEAD) &&
296 !((1 << sk->sk_state) & (TCPF_SYN_SENT | TCPF_SYN_RECV))) {
297 /* Receiver dastardly shrinks window. Our retransmits
298 * become zero probes, but we should not timeout this
299 * connection. If the socket is an orphan, time it out,
300 * we cannot allow such beasts to hang infinitely.
301 */
302 #ifdef TCP_DEBUG
303 struct inet_sock *inet = inet_sk(sk);
304 if (sk->sk_family == AF_INET) {
305 LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI4:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
306 &inet->daddr, ntohs(inet->dport),
307 inet->num, tp->snd_una, tp->snd_nxt);
308 }
309 #if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE)
310 else if (sk->sk_family == AF_INET6) {
311 struct ipv6_pinfo *np = inet6_sk(sk);
312 LIMIT_NETDEBUG(KERN_DEBUG "TCP: Peer %pI6:%u/%u unexpectedly shrunk window %u:%u (repaired)\n",
313 &np->daddr, ntohs(inet->dport),
314 inet->num, tp->snd_una, tp->snd_nxt);
315 }
316 #endif
317 #endif
318 if (tcp_time_stamp - tp->r