zoukankan      html  css  js  c++  java
  • TCP输入 之 tcp_data_queue

    tcp_data_queue作用为数据段的接收处理,其中分为多种情况:

    (1) 无数据,释放skb,返回;

    (2) 预期接收的数据段,a. 进行0窗口判断;b. 进程上下文,复制数据到用户空间;c. 不满足b或者b未完整拷贝此skb的数据段,则加入到接收队列;d. 更新下一个期望接收的序号;e. 若有fin标记,则处理fin;f. 乱序队列不为空,则处理乱序;g. 快速路径的检查和设置;h. 唤醒用户空间进程读取数据;

    (3) 重传的数据段,进入快速ack模式,释放该skb;

    (4) 窗口以外的数据段,进入快速ack模式,释放该skb;

    (5) 数据段重叠,在进行0窗口判断之后,进行(2)中的加入接收队列,以及>=d的流程;

    (6) 乱序的数据段,调用tcp_data_queue_ofo进行乱序数据段的接收处理;

      1 static void tcp_data_queue(struct sock *sk, struct sk_buff *skb)
      2 {
      3     struct tcp_sock *tp = tcp_sk(sk);
      4     bool fragstolen = false;
      5     int eaten = -1;
      6 
      7     /* 无数据 */
      8     if (TCP_SKB_CB(skb)->seq == TCP_SKB_CB(skb)->end_seq) {
      9         __kfree_skb(skb);
     10         return;
     11     }
     12 
     13     /* 删除路由缓存 */
     14     skb_dst_drop(skb);
     15 
     16     /* 去掉tcp首部 */
     17     __skb_pull(skb, tcp_hdr(skb)->doff * 4);
     18 
     19     tcp_ecn_accept_cwr(tp, skb);
     20 
     21     tp->rx_opt.dsack = 0;
     22 
     23     /*  Queue data for delivery to the user.
     24      *  Packets in sequence go to the receive queue.
     25      *  Out of sequence packets to the out_of_order_queue.
     26      */
     27     /* 预期接收的数据段 */
     28     if (TCP_SKB_CB(skb)->seq == tp->rcv_nxt) {
     29         /* 窗口为0,不能接收数据 */
     30         if (tcp_receive_window(tp) == 0)
     31             goto out_of_window;
     32 
     33         /* Ok. In sequence. In window. */
     34         /* 进程上下文 */
     35 
     36         /* 当前进程读取数据 */
     37         if (tp->ucopy.task == current &&
     38             /* 用户空间读取序号与接收序号一致&& 需要读取的数据不为0 */
     39             tp->copied_seq == tp->rcv_nxt && tp->ucopy.len &&
     40             /* 被用户空间锁定&& 无紧急数据 */
     41             sock_owned_by_user(sk) && !tp->urg_data) {
     42 
     43             /* 带读取长度和数据段长度的较小值 */
     44             int chunk = min_t(unsigned int, skb->len,
     45                       tp->ucopy.len);
     46             /* 设置running状态 */
     47             __set_current_state(TASK_RUNNING);
     48 
     49             /* 拷贝数据 */
     50             if (!skb_copy_datagram_msg(skb, 0, tp->ucopy.msg, chunk)) {
     51                 tp->ucopy.len -= chunk;
     52                 tp->copied_seq += chunk;
     53                 /* 完整读取了该数据段 */
     54                 eaten = (chunk == skb->len);
     55 
     56                 /* 调整接收缓存和窗口 */
     57                 tcp_rcv_space_adjust(sk);
     58             }
     59         }
     60 
     61         /* 未拷贝到用户空间或者未拷贝完整数据段 */
     62         if (eaten <= 0) {
     63 queue_and_out:
     64             /* 没有拷贝到用户空间,对内存进行检查 */
     65             if (eaten < 0) {
     66                 if (skb_queue_len(&sk->sk_receive_queue) == 0)
     67                     sk_forced_mem_schedule(sk, skb->truesize);
     68                 else if (tcp_try_rmem_schedule(sk, skb, skb->truesize))
     69                     goto drop;
     70             }
     71 
     72             /* 添加到接收队列 */
     73             eaten = tcp_queue_rcv(sk, skb, 0, &fragstolen);
     74         }
     75 
     76         /* 更新下一个期望接收的序号*/
     77         tcp_rcv_nxt_update(tp, TCP_SKB_CB(skb)->end_seq);
     78         /* 有数据 */
     79         if (skb->len)
     80             tcp_event_data_recv(sk, skb);
     81 
     82         /* 标记有fin,则处理 */
     83         if (TCP_SKB_CB(skb)->tcp_flags & TCPHDR_FIN)
     84             tcp_fin(sk);
     85 
     86         /* 乱序队列有数据,则处理 */
     87         if (!RB_EMPTY_ROOT(&tp->out_of_order_queue)) {
     88 
     89             /* 将乱序队列中的数据段转移到接收队列 */
     90             tcp_ofo_queue(sk);
     91 
     92             /* RFC2581. 4.2. SHOULD send immediate ACK, when
     93              * gap in queue is filled.
     94              */
     95             /* 乱序数据段处理完毕,需要立即发送ack */
     96             if (RB_EMPTY_ROOT(&tp->out_of_order_queue))
     97                 inet_csk(sk)->icsk_ack.pingpong = 0;
     98         }
     99 
    100         if (tp->rx_opt.num_sacks)
    101             tcp_sack_remove(tp);
    102 
    103         /* 快路检查 */
    104         tcp_fast_path_check(sk);
    105 
    106         /* 向用户空间拷贝了数据,则释放skb */
    107         if (eaten > 0)
    108             kfree_skb_partial(skb, fragstolen);
    109 
    110         /* 不在销毁状态,则唤醒进程读取数据 */
    111         if (!sock_flag(sk, SOCK_DEAD))
    112             sk->sk_data_ready(sk);
    113         return;
    114     }
    115 
    116     /* 重传 */
    117     if (!after(TCP_SKB_CB(skb)->end_seq, tp->rcv_nxt)) {
    118         /* A retransmit, 2nd most common case.  Force an immediate ack. */
    119         NET_INC_STATS(sock_net(sk), LINUX_MIB_DELAYEDACKLOST);
    120         tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, TCP_SKB_CB(skb)->end_seq);
    121 
    122 out_of_window:
    123         /* 进入快速ack模式 */
    124         tcp_enter_quickack_mode(sk);
    125 
    126         /*  调度ack */
    127         inet_csk_schedule_ack(sk);
    128 drop:
    129         /* 释放skb */
    130         tcp_drop(sk, skb);
    131         return;
    132     }
    133 
    134     /* Out of window. F.e. zero window probe. */
    135     /* 窗口以外的数据,比如零窗口探测报文段 */
    136     if (!before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt + tcp_receive_window(tp)))
    137         goto out_of_window;
    138 
    139     /* 进入快速ack模式 */
    140     tcp_enter_quickack_mode(sk);
    141 
    142     /* 数据段重叠 */
    143     if (before(TCP_SKB_CB(skb)->seq, tp->rcv_nxt)) {
    144         /* Partial packet, seq < rcv_next < end_seq */
    145         SOCK_DEBUG(sk, "partial packet: rcv_next %X seq %X - %X
    ",
    146                tp->rcv_nxt, TCP_SKB_CB(skb)->seq,
    147                TCP_SKB_CB(skb)->end_seq);
    148 
    149         tcp_dsack_set(sk, TCP_SKB_CB(skb)->seq, tp->rcv_nxt);
    150 
    151         /* If window is closed, drop tail of packet. But after
    152          * remembering D-SACK for its head made in previous line.
    153          */
    154         /* 窗口为0,不能接收 */
    155         if (!tcp_receive_window(tp))
    156             goto out_of_window;
    157         goto queue_and_out;
    158     }
    159 
    160     /* 接收乱序数据段 */
    161     tcp_data_queue_ofo(sk, skb);
    162 }
  • 相关阅读:
    java冒泡算法
    java时间操作
    Java重写构造方法
    正则Sub用法
    Python正则反向引用
    Django发送邮件
    Django导出excel
    Nginx编译安装
    年薪20万Python工程师进阶(7):Python资源大全,让你相见恨晚的Python库
    Go语言学习笔记
  • 原文地址:https://www.cnblogs.com/wanpengcoder/p/11752133.html
Copyright © 2011-2022 走看看