zoukankan      html  css  js  c++  java
  • (3) linux 3.x


    http://blog.csdn.net/zhangskd/article/details/47380761


    主要内容:客户端接收SYNACK、发送ACK,完成连接的建立。

    内核版本:3.15.2

    我的博客:http://blog.csdn.net/zhangskd

    接收入口

    tcp_v4_rcv

        |--> tcp_v4_do_rcv

                   |-> tcp_rcv_state_process

                             |-> tcp_rcv_synsent_state_process

    1. 状态为ESTABLISHED时,用tcp_rcv_established()接收处理。

    2. 状态为LISTEN时,说明这个sock处于监听状态,用于被动打开的接收处理,包括SYN和ACK。

    3. 当状态不为ESTABLISHED或TIME_WAIT时,用tcp_rcv_state_process()处理。

    客户端主动建立连接时,发送SYN段后,连接的状态变为SYN_SENT。

    此时如果收到SYNACK段,处理函数为tcp_rcv_state_process()。

    1. int tcp_v4_do_rcv(struct sock *sk, struct sk_buff *skb)  
    2. {  
    3.     struct sock *rsk;  
    4.   
    5. #ifdef CONFIG_TCP_MD5SIG  
    6.     /* We really want to reject the packet as early as possible if : 
    7.      * We're expecting an MD5'd packet and this is no MD5 tcp option. 
    8.      * There is an MD5 option and we're not expecting one. 
    9.      */  
    10.     if (tcp_v4_inbound_md5_hash(sk, skb))  
    11.         goto discard;  
    12. #endif  
    13.   
    14.     /* 当状态为ESTABLISHED时,用tcp_rcv_established()接收处理 */  
    15.     if (sk->sk_state == TCP_ESTABLISHED) { /* Fast path */  
    16.         struct dst_entry *dst = sk->sk_rx_dst;  
    17.         sock_rps_save_rxhash(sk, skb);  
    18.   
    19.         if (dst) {  
    20.             if (inet_sk(sk)->rx_dst_ifindex != skb->skb_iif || dst->ops->check(dst, 0) == NULL) {  
    21.                 dst_release(dst);  
    22.                 sk->sk_rx_dst = NULL;  
    23.             }  
    24.         }  
    25.    
    26.         /* 连接已建立时的处理路径 */  
    27.         tcp_rcv_established(sk, skb, tcp_hdr(skb), skb->len);  
    28.         return 0;  
    29.     }  
    30.   
    31.     /* 检查报文长度、报文校验和 */  
    32.     if (skb->len < tcp_hdrlen(skb) || tcp_checksum_complete(skb))  
    33.         goto csum_err;  
    34.   
    35.     /* 如果这个sock处于监听状态,被动打开时的处理,包括收到SYN或ACK */  
    36.     if (sk->sk_state == TCP_LISTEN) {  
    37.         /* 返回值: 
    38.          * NULL,错误 
    39.          * nsk == sk,接收到SYN 
    40.          * nsk != sk,接收到ACK 
    41.          */  
    42.         struct sock *nsk = tcp_v4_hnd_req(sk, skb);  
    43.   
    44.         if (! nsk)  
    45.             goto discard;  
    46.   
    47.         if (nsk != sk) { /* 接收到ACK时 */  
    48.             sock_rps_save_rxhash(nsk, skb);  
    49.   
    50.             if (tcp_child_process(sk, nsk, skb)) { /* 处理新的sock */  
    51.                 rsk = nsk;  
    52.                 goto reset;  
    53.             }  
    54.             return 0;  
    55.         }  
    56.     } else  
    57.         sock_rps_save_rx(sk, skb);  
    58.   
    59.     /* 处理除了ESTABLISHED和TIME_WAIT之外的所有状态,包括SYN_SENT状态 */  
    60.     if (tcp_rcv_state_process(sk, skb, tcp_hdr(skb), skb->len)) {  
    61.         rsk = sk;  
    62.         goto reset;  
    63.     }  
    64.     return 0;  
    65.   
    66. reset:  
    67.     tcp_v4_send_reset(rsk, skb); /* 发送被动的RST包 */  
    68.   
    69. discard:  
    70.     kfree_skb(skb);  
    71.     return 0;  
    72.   
    73. csum_err:  
    74.     TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_CSUMERRORS);  
    75.     TCP_INC_STATS_BH(sock_net(sk), TCP_MIB_INERRS);  
    76.     goto discard;  
    77. }  

    连接状态不为ESTABLISHED或TIME_WAIT时的处理函数为tcp_rcv_state_process()。

    1. /* This function implements the receiving procedure of RFC 793 for  
    2.  * all states except ESTABLISHED and TIME_WAIT. 
    3.  */  
    4.   
    5. int tcp_rcv_state_process(struct sock *sk, struct sk_buff *skb, const struct tcphdr *th, unsigned int len)  
    6. {  
    7.     struct tcp_sock *tp = tcp_sk(sk);  
    8.     struct inet_connection_sock *icsk = inet_csk(sk);  
    9.     struct request_sock *req;  
    10.     int queued = 0;  
    11.     bool acceptable;  
    12.     u32 synack_stamp;  
    13.   
    14.     tp->rx_opt.saw_tstamp = 0;  
    15.   
    16.     switch (sk->sk_state) {  
    17.     ...  
    18.   
    19.     case TCP_SYN_SENT:  
    20.   
    21.         /* 处理SYN_SENT状态,主要做了: 
    22.          * 判断SYNACK的合法性,更新连接的信息。 
    23.          * 把连接状态置为TCP_ESTABLISHED。 
    24.          * 发送ACK,可能立即发送,也可能延迟发送。 
    25.          */  
    26.         queued = tcp_rcv_synsent_state_process(sk, skb, th, len);  
    27.   
    28.         if (queued >= 0)  
    29.             return queued; /* 会导致调用函数发送RST */  
    30.           
    31.         tcp_urg(sk, skb, th); /* 处理紧急数据 */  
    32.   
    33.         /* 发送数据,并检查是否需要扩大发送缓存 */  
    34.         tcp_data_snd_check(sk);  
    35.   
    36.         return 0;  
    37.     }  
    38.     ...  
    39. }  

    SYN_SENT状态处理

    tcp_rcv_synsent_state_process()用于SYN_SENT状态的处理,具体又分两种场景。

    (1) 接收到SYNACK

    一般情况下会收到服务端的SYNACK,处理如下:

    检查ack_seq是否合法。

    如果使用了时间戳选项,检查回显的时间戳是否合法。

    检查TCP的标志位是否合法。

    如果SYNACK是合法的,更新sock的各种信息。   

    把连接的状态设置为TCP_ESTABLISHED,唤醒调用connect()的进程。

    判断是马上发送ACK,还是延迟发送。

    (2) 接收到SYN

    本端之前发送出一个SYN,现在又接收到了一个SYN,双方同时向对端发起建立连接的请求。

    处理如下:

    把连接状态置为SYN_RECV。

    更新sock的各种信息。

    构造和发送SYNACK。

    接者对端也会回应SYNACK,之后的处理流程和服务器端接收ACK类似,可参考之前的blog。

    当tcp_rcv_synsent_state_process()的返回值大于0时,会导致上层调用函数发送一个被动的RST。 

    Q:那么什么情况下此函数的返回值会大于0?

    A:收到一个ACK段,但ack_seq的序号不正确,或者回显的时间戳不正确。

    1. static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,  
    2.     const struct tcphdr *th, unsigned int len)  
    3. {  
    4.     struct inet_connection_sock *icsk = inet_csk(sk);  
    5.     struct tcp_sock *tp = tcp_sk(sk);  
    6.     struct tcp_fastopen_cookie foc = { .len = -1 };  
    7.     int saved_clamp = tp->rx_opt.mss_clamp;  
    8.   
    9.     /* 全面解析skb携带的TCP选项 */  
    10.     tcp_parse_options(skb, &tp->rx_opt, 0, &foc);  
    11.     if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr)  
    12.         tp->rx_opt.rcv_tsecr -= tp->tsoffset; /* timestamp offset */  
    13.   
    14.     /* 如果携带ACK标志,那么有可能是SYNACK */  
    15.     if (th->ack) {  
    16.         /* rfc793: 
    17.          * If the state is SYN-SENT then first check the ACK bit 
    18.          *     If the ACK bit is set 
    19.          *     If the SEG.ACK <= ISS, or SEG.ACK > SND.NXT, send 
    20.          *     a reset (unless the RST bit is set, if so drop the segment 
    21.          *     and return)" 
    22.          */  
    23.         /* 检查ack_seq:snd_una < ack_seq <= snd_nxt。 
    24.          * 如果SYN段没有携带数据,那么此时ack_seq应该为本端的ISN + 1。 
    25.          */  
    26.         if (! after(TCP_SKB_CB(skb)->ack_seq, tp->snd_una) ||  
    27.             after(TCP_SKB_CB(skb)->ack_seq, tp->snd_nxt))  
    28.             goto reset_and_undo;  
    29.   
    30.         /* 如果使用了时间戳选项,那么回显的时间戳,必须落在 
    31.          * 第一次发送SYN段的时间和当前时间之间。 
    32.          */  
    33.         if (tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&  
    34.             !between(tp->rx_opt.rcv_tsecr, tp->retrans_stamp, tcp_time_stamp)) {  
    35.             NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_PAWSACTIVEREJECTED);  
    36.             goto reset_and_undo;  
    37.         }  
    38.   
    39.         /* Now ACK is acceptable. 
    40.          * If the RST bit is set 
    41.          *     If the ACK was acceptable then signal the user "error: connection reset", 
    42.          *     drop the segment, enter CLOSED state, delete TCB, and return." 
    43.          */  
    44.         if (th->rst) { /* 如果携带了RST标志位,那么建立连接失败了:)*/  
    45.             tcp_reset(sk);  
    46.             goto discard;  
    47.         }  
    48.   
    49.         /* RFC793: 
    50.          * fifth, if neither of the SYN or RST bits is set then drop the segment and return. 
    51.          */  
    52.         /* 如果既没有RST也没有SYN标志位,那么直接丢弃这个ACK */  
    53.         if (! th->syn)  
    54.             goto discard_and_undo;  
    55.   
    56.         /* RFC793: 
    57.          * If the SYN bit is on ... 
    58.          * are acceptable then ... 
    59.          * (ousr SYN has been ACKed), change the connection state to ESTABLISHED... 
    60.          */  
    61.         /* 收到一个合法的SYNACK了,接下来要完成连接的建立了 */  
    62.   
    63.         /* 如果对端支持ECN,SYNACK只会设置ECE标志。 
    64.          * 否则,连接就不支持ECN显式拥塞通知了。 
    65.          */  
    66.         TCP_ECN_rcv_synack(tp, th);  
    67.   
    68.         /* 记录最近更新发送窗口的ACK序号 */  
    69.         tcp_init_wl(tp, TCP_SKB_CB(skb)->seq);  
    70.   
    71.         /* 更新发送窗口,删除发送队列中已被确认的SYN段,并进行时延采样 */  
    72.         tcp_ack(sk, skb, FLAG_SLOWPATH);  
    73.   
    74.         /* Ok. it's good. Set up sequence numbers and move to established. */  
    75.         tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1/* 更新接收窗口的要接收的下一个序号 */  
    76.         tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1/* 更新接收窗口的左端 */  
    77.   
    78.         /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. 
    79.          * 更新对端接收窗口的大小。在三次握手时,不使用窗口扩大因子。 
    80.          */  
    81.         tp->snd_wnd = ntohs(th->window);  
    82.   
    83.         /* 如果连接不支持窗口扩大因子选项 */  
    84.         if (! tp->rx_opt.wscale_ok) {  
    85.             tp->rx_opt.snd_wscale = tp->rx_opt.rcv_wscale = 0;  
    86.             tp->window_clamp = min(tp->window_clamp, 65535U);  
    87.         }  
    88.   
    89.         /* 如果连接支持时间戳选项 */  
    90.         if (tp->rx_opt.saw_tstamp) {  
    91.             tp->rx_opt.tstamp_ok = 1;  
    92.             tp->tcp_header_len = sizeof(struct tcphdr) + TCPOLEN_TSTAMP_ALIGNED;  
    93.             tp->advmss -= TCPOLEN_TSTAMP_ALIGNED;  
    94.             tcp_store_ts_recent(tp); /* 记录对端的时间戳,作为下次发送的回显值 */  
    95.         } else {  
    96.             tp->tcp_header_len = sizeof(struct tcphdr);  
    97.         }  
    98.   
    99.         /* 使用SACK时,才能考虑是否使用FACK */  
    100.         if (tcp_is_sack(tp) && sysctl_tcp_fack)  
    101.             tcp_enable_fack(tp);  
    102.   
    103.         tcp_mtu_init(sk); /* TCP的MTU初始化 */  
    104.         tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); /* 更新MSS */  
    105.         tcp_initialize_rcv_mss(sk); /* 对端有效发送MSS估值的初始化 */  
    106.   
    107.         /* Remember, tcp_poll() does not lock socket! 
    108.          * Change state from SYN-SENT only after copied_seq is initialized. 
    109.          */  
    110.         tp->copied_seq = tp->rcv_nxt; /* 更新未读数据的左端 */  
    111.   
    112.         smp_mb();  
    113.   
    114.         /* 走到这里,连接算是成功建立了,接下来: 
    115.          * 把连接的状态设置为TCP_ESTABLISHED。 
    116.          * 唤醒调用connect()的进程。 
    117.          */  
    118.         tcp_finish_connect(sk, skb);  
    119.   
    120.         /* Fast Open选项处理 */  
    121.         if ((tp->syn_fastopen || tp->syn_data) &&  
    122.             tcp_rcv_fastopen_synack(sk, skb, &foc))  
    123.             return -1;  
    124.   
    125.         /* 符合以下任一条件,则使用延迟确认,不会马上发送ACK: 
    126.          * 目前有数据等待发送。 
    127.          * 使用TCP_DEFER_ACCEPT选项。 
    128.          * 延迟确认标志为1。 
    129.          */  
    130.         if (sk->sk_write_pending || icsk->icsk_accept_queue->rskq_defer_accept ||  
    131.             icsk->icsk_ack.pingpong) {  
    132.             inet_csk_schedule_ack(sk); /* 设置ICSK_ACK_SCHED标志位,表示有ACK需要发送 */  
    133.             icsk->icsk_ack.lrcvtime = tcp_time_stamp; /* 更新最后一次接收到数据报的时间 */  
    134.             tcp_enter_quickack_mode(sk); /* 进入快速确认模式,之后会进行快速确认 */  
    135.   
    136.             /* 激活延迟确认定时器,超时时间为200ms,也就是说最多延迟200ms */  
    137.             inet_csk_reset_xmit_timer(sk, ICSK_TIME_DACK, TCP_DELACK_MAX, TCP_RTO_MAX);  
    138.   
    139. discard:  
    140.             __kfree_skb(skb);  
    141.             return 0;  
    142.   
    143.         } else {  
    144.             tcp_send_ack(sk); /* 立即发送一个ACK,即三次握手的最后一个ACK */  
    145.         }   
    146.   
    147.         return -1;  
    148.     }  
    149.   
    150.   
    151.     /* No ACK in the segment */  
    152.   
    153.     /* 如果收到的段没有ACK标志,却设置了RST标志,那么直接丢掉 */  
    154.     if (th->rst) {  
    155.         /* rfc793: 
    156.          * If the RST bit is set and no ACK, drop the segment and return. 
    157.          */  
    158.         goto discard_and_undo;  
    159.     }  
    160.   
    161.     /* PAWS check. 检查时间戳是否合法 */  
    162.     if (tp->rx_opt.ts_recent_tstamp && tp->rx_opt.saw_tstamp &&  
    163.         tcp_paws_reject(&tp->rx_opt, 0))  
    164.         goto discard_and_undo;  
    165.   
    166.     /* 收到了SYN段,即同时打开 */  
    167.     if (th->syn) {  
    168.   
    169.         /* We see SYN without ACK. It is attempt of simultaneous connect 
    170.          * with crossed SYNs. Particularly, it can be connect to self. 
    171.          */  
    172.          /* 发送SYN后,状态为SYN_SENT,如果此时也收到SYN, 
    173.           * 状态则变为SYN_RECV。 
    174.           */  
    175.         tcp_set_state(sk, TCP_SYN_RECV);  
    176.   
    177.         if (tp->rx_opt.saw_tstamp) {  
    178.             tp->rx_opt.tstamp_ok = 1;  
    179.             tcp_store_ts_recent(tp); /* 记录对端的时间戳,作为下次发送的回显值 */  
    180.             tp->tcp_header_len = sizeof(tcphdr) + TCPOLEN_TSTAMP_ALIGNED;  
    181.         } else {  
    182.             tp->tcp_header_len = sizeof(struct tcphdr);  
    183.         }  
    184.   
    185.         tp->rcv_nxt = TCP_SKB_CB(skb)->seq + 1/* 更新接收窗口的要接收的下一个序号 */  
    186.         tp->rcv_wup = TCP_SKB_CB(skb)->seq + 1/* 更新接收窗口的左端 */  
    187.   
    188.         /* RFC1323: The window in SYN & SYN/ACK segments is never scaled. 
    189.          * 更新对端接收窗口的大小。在三次握手时,不使用窗口扩大因子。 
    190.          */  
    191.         tp->snd_wnd = ntohs(th->window);  
    192.         tp->snd_wl1 = TCP_SKB_CB(skb)->seq;  /* 记录最近更新发送窗口的ACK序号 */  
    193.         tp->max_window = tp->snd_wnd; /* 目前见过的对端的最大通告窗口 */  
    194.   
    195.         /* 如果对端支持ECN,SYN会同时设置ECE和CWR标志。 
    196.          * 否则,连接就不支持ECN显式拥塞通知了。 
    197.          */  
    198.         TCP_ECN_rcv_syn(tp, th);  
    199.   
    200.         tcp_mtu_init(sk); /* TCP的MTU初始化 */  
    201.         tcp_sync_mss(sk, icsk->icsk_pmtu_cookie); /* 更新MSS */  
    202.         tcp_initialize_rcv_mss(sk); /* 对端有效发送MSS估值的初始化 */  
    203.   
    204.         /* 构造和发送SYNACK */  
    205.         tcp_send_synack(sk);   
    206.   
    207.         goto discard;  
    208.     }  
    209.   
    210. discard_and_undo:  
    211.     tcp_clear_options(&tp->rx_opt);  
    212.     tp->rx_opt.mss_clamp = saved_clamp;  
    213.     goto discard;  
    214.   
    215. reset_and_undo:  
    216.     tcp_clear_options(&tp->rx_opt);  
    217.     tp->rx_opt.mss_clamp = saved_clamp;  
    218.     return 1;  
    219. }  

    同时打开时,在SYN_SENT状态,收到SYN段后,状态变为SYN_RECV,然后发送SYNACK。

    之后如果收到合法的SYNACK后,就能完成连接的建立。

    1. /* Send a crossed SYN-ACK during socket establishment. 
    2.  * WARNING: This routine must only be called when we have already 
    3.  * sent a SYN packet that crossed the incoming SYN that caused this 
    4.  * routine to get called. If this assumption fails then the initial rcv_wnd 
    5.  * and rcv_wscale values will not be correct. 
    6.  */  
    7.   
    8. int tcp_send_synack(struct sock *sk)  
    9. {  
    10.     struct sk_buff *skb;  
    11.   
    12.     skb = tcp_write_queue_head(sk); /* 发送队列的第一个段,即SYN段 */  
    13.   
    14.     if (skb == NULL || ! (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_SYN)) {  
    15.         pr_debug("%s: wrong queue state ", __func__);  
    16.         return -EFAULT;  
    17.     }  
    18.    
    19.     if (! (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_ACK)) {  
    20.   
    21.         /* 如果这个skb是克隆的,并且有多个使用者,那么就不能直接修改此skb。 
    22.          * 此时再克隆一个私有的nskb,替换掉之前的。然后就可以任意修改了。 
    23.          */  
    24.         if (skb_cloned(skb)) {  
    25.             struct sk_buff *nskb = skb_copy(skb, GFP_ATOMIC); /* 再克隆一份 */  
    26.             if (nskb == NULL)  
    27.                 return -ENOMEM;  
    28.   
    29.             tcp_unlink_write_queue(skb, sk); /* 把skb从发送队列中删除 */  
    30.             skb_header_release(nskb); /* 增加skb负荷部分的引用计数 */  
    31.             __tcp_add_write_queue_head(sk, nskb); /* 把nskb放入发送队列的头部 */  
    32.             sk_wmem_free_skb(sk, skb); /* 更新内存使用情况 */  
    33.             sk->sk_wmem_queued += nskb->truesize; /* 更新发送队列的总大小 */  
    34.             sk_mem_charge(sk, nskb->truesize); /* 更新预分配但未使用的内存大小 */  
    35.             skb = nskb; /* 接下来使用的是独占的nskb */  
    36.         }  
    37.   
    38.         TCP_SKB_CB(skb)->tcp_flags |= TCPHDR_ACK;  
    39.         TCP_ECN_send_synack(tcp_sk(sk), skb); /* 设置ECN标志位 */  
    40.     }  
    41.   
    42.     TCP_SKB_CB(skb)->when = tcp_time_stamp;  
    43.     return tcp_transmit_skb(sk, skb, 1, GFP_ATOMIC); /* 发送此SYNACK段 */  
    44. }   
    45.   
    46. static inline void sk_wmem_free_skb(struct sock *sk, struct sk_buff *skb)  
    47. {  
    48.     /* write queue has been shrunk recently */  
    49.     sock_set_flag(sk, SOCK_QUEUE_SHRUNK);  
    50.     sk->sk_wmem_queued -= skb->truesize; /* 更新发送队列的总大小 */  
    51.     sk_mem_uncharge(sk, skb->truesize); /* 更新预分配但未使用的内存大小 */  
    52.     __kfree_skb(skb);  
    53. }  

    唤醒用户进程

    tcp_finish_connect()用来完成连接的建立,主要做了以下事情:

    1. 把连接的状态从SYN_SENT置为ESTABLISHED。

    2. 根据路由缓存,初始化TCP相关的变量。

    3. 获取默认的拥塞控制算法

    4. 调整发送缓存和接收缓存的大小。

    5. 如果使用了SO_KEEPALIVE选项,激活保活定时器。

    6. 唤醒此socket等待队列上的进程(即调用connect的进程)。

        如果使用了异步通知,则发送SIGIO通知异步通知队列上的进程可写。

    1. void tcp_finish_connect(struct sock *sk, struct sk_buff *skb)  
    2. {  
    3.     struct tcp_sock *tp = tcp_sk(sk);  
    4.     struct inet_connection_sock *icsk = inet_csk(sk);  
    5.   
    6.     /* 连接状态从SYN_SENT变为ESTABLISHED */  
    7.     tcp_set_state(sk, TCP_ESTABLISHED);  
    8.   
    9.     if (skb != NULL) {  
    10.         icsk->icsk_af_ops->sk_rx_dst_set(sk, skb);  
    11.         security_inet_conn_established(sk, skb);  
    12.     }  
    13.   
    14.     /* Make sure socket is routed, for correct metrics */  
    15.     icsk->icsk_af_ops->rebuild_header(sk);  
    16.   
    17.     /* 根据路由缓存,初始化TCP相关变量 */  
    18.     tcp_init_metrics(sk);  
    19.   
    20.     /* 获取默认的TCP拥塞控制算法 */  
    21.     tcp_init_congestion_control(sk);  
    22.   
    23.     /* Prevent spurious tcp_cwnd_restart() on first data packet. */  
    24.     tp->lsndtime = tcp_time_stamp; /* 最近发包的时间 */  
    25.   
    26.     /* 调整发送缓存和接收缓存的大小 */  
    27.     tcp_init_buffer_space(sk);  
    28.   
    29.     /* 如果使用了SO_KEEPALIVE选项,激活保活定时器 */  
    30.     if (sock_flag(sk, SOCK_KEEPOPEN))  
    31.         inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tp));  
    32.   
    33.     /* 如果对端的窗口扩大因子为0 */  
    34.     if (! tp->rx_opt.snd_wscale)  
    35.         __tcp_fast_path_on(tp, tp->snd_wnd); /* 设置首部预测字段 */  
    36.     else  
    37.         tp->pred_flags = 0;  
    38.   
    39.     if (! sock_flag(sk, SOCK_DEAD)) {  
    40.         /* 指向sock_def_wakeup,唤醒调用connect()的进程 */  
    41.         sk->sk_state_change(sk);   
    42.   
    43.         /* 如果使用了异步通知,则发送SIGIO通知进程可写 */  
    44.         sk_wake_async(sk, SOCK_WAKE_IO, POLL_OUT);  
    45.     }  
    46. }  

  • 相关阅读:
    关键字static
    关键字const有什么含义?
    关于目标
    B/B+树的初步理解(一)
    优先队列(priority_queue)
    哨兵的作用
    数学笑话集(一)

    排序算法小结(一)
    KMP算法
  • 原文地址:https://www.cnblogs.com/ztguang/p/12644868.html
Copyright © 2011-2022 走看看