MST

星途 面试题库

面试题:网络编程下TCP协议重传机制在协议栈源码中的体现

在TCP/IP协议栈源码中,如何实现TCP的重传机制?重传超时时间是如何计算和调整的?请结合具体源码片段进行分析。
25.1万 热度难度
后端开发网络编程

知识考点

AI 面试

面试题答案

一键面试

TCP重传机制的实现

  1. 重传定时器:在TCP协议栈中,每个TCP连接都维护一个重传定时器(Retransmission Timer, RTO)。当TCP发送一个报文段后,会启动这个定时器。如果在定时器超时之前没有收到对应的确认(ACK),则认为该报文段丢失,需要重传。
    • 例如在Linux内核源码(以较新的版本为例,如5.x内核)中,tcp_output.c文件中的tcp_transmit_skb函数,在发送报文段时会调用tcp_reset_xmit_timer函数启动重传定时器。
    int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
                         gfp_t gfp_mask)
    {
        // 发送报文段相关操作
        tcp_reset_xmit_timer(sk, TCP_WRITE_TIMER, tcp_time_stamp + tcp_rto(sk), TCP_RTO_MAX);
        return 1;
    }
    
  2. 重传队列:TCP会将已发送但未确认的报文段放入重传队列(retransmission queue)。当需要重传时,从这个队列中取出报文段进行重传。
    • 在Linux内核中,tcp_out.c文件里有相关数据结构和操作来管理重传队列。struct tcp_sock结构体中有成员用于记录重传队列相关信息,例如retrans_out记录重传队列中的字节数等。
    struct tcp_sock {
        // 其他成员
        unsigned int retrans_out;
        // 更多与重传队列相关的成员
    };
    
  3. 重传策略
    • 快速重传:当TCP接收到多个重复的ACK时(通常是3个重复ACK),认为可能发生了报文段丢失,但网络状况可能还不错,此时会触发快速重传机制,不等重传定时器超时就重传丢失的报文段。在Linux内核中,tcp_input.c文件的tcp_fastretrans_alert函数处理快速重传相关逻辑。
    void tcp_fastretrans_alert(struct sock *sk, struct sk_buff *skb, int flag)
    {
        // 处理快速重传逻辑,如重传报文段等操作
    }
    
    • 超时重传:如果重传定时器超时,TCP会从重传队列中重传最早未确认的报文段,并重新启动重传定时器,且一般会加倍重传超时时间(指数退避)。在tcp_timer.c文件的tcp_write_timer函数中处理超时重传逻辑。
    void tcp_write_timer(struct sock *sk)
    {
        // 检查重传队列,重传最早未确认的报文段
        tcp_reset_xmit_timer(sk, TCP_WRITE_TIMER, tcp_time_stamp + tcp_rto(sk), TCP_RTO_MAX);
    }
    

重传超时时间(RTO)的计算和调整

  1. 初始RTO的计算:在TCP连接建立时,会初始化RTO。通常初始RTO有一个默认值,例如在Linux内核中,默认初始RTO为3秒(TCP_RTO_MIN,在include/net/tcp.h中定义)。
    #define TCP_RTO_MIN ((unsigned)(HZ/3)) /* 300ms */
    
  2. 基于往返时间(RTT)的计算:TCP会不断测量报文段的往返时间(Round - Trip Time, RTT)。每次收到ACK时,会根据新测量的RTT来调整RTO。
    • 平滑往返时间(Smoothed RTT, SRTT)的计算:SRTT = (1 - α) * SRTT + α * RTT,其中α是一个平滑因子,通常取值为1/8。
    • 偏差(Deviation, RTTD)的计算:RTTD = (1 - β) * RTTD + β * |RTT - SRTT|,β通常取值为1/4。
    • RTO的计算:RTO = SRTT + 4 * RTTD
    • 在Linux内核中,tcp_input.c文件的tcp_time_stamp函数在收到ACK时更新RTT相关统计信息,进而影响RTO的计算。
    void tcp_time_stamp(struct sock *sk, struct sk_buff *skb, int flag)
    {
        // 计算和更新RTT、SRTT、RTTD等相关值,影响RTO计算
    }
    
  3. RTO的调整
    • 指数退避:当发生超时重传时,RTO会加倍(指数退避),以避免在网络拥塞时频繁重传加重网络负担。例如在tcp_timer.c文件的tcp_write_timer函数中,当超时重传发生时,会调用tcp_ca_event函数,在拥塞控制算法中会对RTO进行加倍等调整。
    void tcp_write_timer(struct sock *sk)
    {
        // 重传操作
        tcp_ca_event(sk, TCP_EVENT_RETRANS_TIMEOUT);
    }
    
    • 快速恢复:在快速重传后进入快速恢复阶段,RTO可能会根据具体的拥塞控制算法(如TCP Reno、TCP NewReno等)进行调整,一般不会进行指数退避那样大幅度的增长,而是相对温和地调整以适应网络状况。