TCP发送源码学习(3)--tcp_transmit_skb

3370阅读 0评论2013-08-21 slp195
分类:LINUX

一、tcp_transmit_skb
  1. static int tcp_transmit_skb(struct sock *sk, struct sk_buff *skb, int clone_it,
  2.              gfp_t gfp_mask)
  3. {
  4.     const struct inet_connection_sock *icsk = inet_csk(sk);
  5.     struct inet_sock *inet;
  6.     struct tcp_sock *tp;
  7.     struct tcp_skb_cb *tcb;
  8.     struct tcp_out_options opts;
  9.     unsigned tcp_options_size, tcp_header_size;
  10.     struct tcp_md5sig_key *md5;
  11.     struct tcphdr *th;
  12.     int err;

  13.     BUG_ON(!skb || !tcp_skb_pcount(skb));

  14.     /* If congestion control is doing timestamping, we must
  15.      * take such a timestamp before we potentially clone/copy.
  16.      */
  17.   /*如果拥塞控制需要做时间才有,则必须在克隆或者拷贝报文之前设置一个时间戳。
  18.     linux支持了多达十种拥塞控制算法,但并不是每种算中都需要做时间采样的,
  19.     因此在设置时间戳前先判断当前的拥塞算法是否需要做时间采样。*/
  20.     if (icsk->icsk_ca_ops->flags & TCP_CONG_RTT_STAMP)
  21.         __net_timestamp(skb);

  22.     /*根据传递进来的clone_it参数来确定是否需要克隆待发送的报文。*/
  23.     if (likely(clone_it)) {
  24.         /*如果skb已经被clone,则只能复制该skb的数据到新分配的skb中*/
  25.         if (unlikely(skb_cloned(skb)))
  26.             skb = pskb_copy(skb, gfp_mask);
  27.         else
  28.         /*clone新的skb*/
  29.             skb = skb_clone(skb, gfp_mask);
  30.         if (unlikely(!skb))
  31.             return -ENOBUFS;
  32.     }

  33.     /*获取INET层和TCP层的传输控制块、skb中的TCP私有数据块。*/
  34.     inet = inet_sk(sk);
  35.     tp = tcp_sk(sk);
  36.     tcb = TCP_SKB_CB(skb);
  37.     memset(&opts, 0, sizeof(opts));

  38.     /*根据TCP选项重新调整TCP首部的长度。*/
  39.     /*判断当前TCP报文是否是SYN段,因为有些选项只能出现在SYN报文中,需做特别处理。*/
  40.     if (unlikely(tcb->flags & TCPHDR_SYN))
  41.         tcp_options_size = tcp_syn_options(sk, skb, &opts, &md5);
  42.     else
  43.         tcp_options_size = tcp_established_options(sk, skb, &opts, &md5);

  44.     /*tcp首部的总长度等于可选长度加上struct tcphdr。*/
  45.     tcp_header_size = tcp_options_size + sizeof(struct tcphdr);

  46.     /*如果已发出但未确认的数据包数目为零,则只初始化拥塞控制,并开始跟踪该连接的RTT。*/
  47.     if (tcp_packets_in_flight(tp) == 0)
  48.         tcp_ca_event(sk, CA_EVENT_TX_START);
  49.     
  50.     /*调用skb_push()在数据部分的头部添加TCP首部,长度即为之前计算得到的那个tcp_header_size,实际上是把data指针往上移。*/
  51.     skb_push(skb, tcp_header_size);
  52.     skb_reset_transport_header(skb);
  53.     /*SKB已添加到发送队列中,但是从SKB的角度去看还不知道他是属于哪个传输控制块,因此调用skb_set_owner_w设置该SKB的宿主。*/
  54.     skb_set_owner_w(skb, sk);

  55.     /* Build TCP header and checksum it. */
  56.     /*填充TCP首部中的源端口source、目的端口dest、TCP报文的序号seq、确认序号ack_seq以及各个标志位*/
  57.     th = tcp_hdr(skb);
  58.     th->source        = inet->inet_sport;
  59.     th->dest        = inet->inet_dport;
  60.     th->seq            = htonl(tcb->seq);
  61.     th->ack_seq        = htonl(tp->rcv_nxt);
  62.     *(((__be16 *)th) + 6)    = htons(((tcp_header_size >> 2) << 12) |
  63.                     tcb->flags);

  64.     /*分两种情况设置TCP首部的接收窗口的大小*/
  65.     if (unlikely(tcb->flags & TCPHDR_SYN)) {
  66.         /* RFC1323: The window in SYN & SYN/ACK segments
  67.          * is never scaled.
  68.          */
  69.         /*如果是SYN段,则设置接收窗口初始值为rcv_wnd*/
  70.         th->window    = htons(min(tp->rcv_wnd, 65535U));
  71.     } else {
  72.         /*如果是其他的报文,则调用tcp_select_window()计算当前接收窗口的大小。*/
  73.         th->window    = htons(tcp_select_window(sk));
  74.     }
  75.     /*初始化TCP首部的校验码和紧急指针,具体请参考TCP协议中的首部定义*/
  76.     th->check        = 0;
  77.     th->urg_ptr        = 0;

  78.     /* The urg_mode check is necessary during a below snd_una win probe */
  79.     if (unlikely(tcp_urg_mode(tp) && before(tcb->seq, tp->snd_up))) {
  80.         if (before(tp->snd_up, tcb->seq + 0x10000)) {
  81.             th->urg_ptr = htons(tp->snd_up - tcb->seq);
  82.             th->urg = 1;
  83.         } else if (after(tcb->seq + 0xFFFF, tp->snd_nxt)) {
  84.             th->urg_ptr = htons(0xFFFF);
  85.             th->urg = 1;
  86.         }
  87.     }

  88.     tcp_options_write((__be32 *)(th + 1), tp, &opts);
  89.     if (likely((tcb->flags & TCPHDR_SYN) == 0))
  90.         TCP_ECN_send(sk, skb, tcp_header_size);

  91. #ifdef CONFIG_TCP_MD5SIG
  92.     /* Calculate the MD5 hash, as we have all we need now */
  93.     if (md5) {
  94.         sk_nocaps_add(sk, NETIF_F_GSO_MASK);
  95.         tp->af_specific->calc_md5_hash(opts.hash_location,
  96.                      md5, sk, NULL, skb);
  97.     }
  98. #endif

  99.     icsk->icsk_af_ops->send_check(sk, skb);

  100.     if (likely(tcb->flags & TCPHDR_ACK))
  101.         tcp_event_ack_sent(sk, tcp_skb_pcount(skb));

  102.     if (skb->len != tcp_header_size)
  103.         tcp_event_data_sent(tp, skb, sk);

  104.     if (after(tcb->end_seq, tp->snd_nxt) || tcb->seq == tcb->end_seq)
  105.         TCP_ADD_STATS(sock_net(sk), TCP_MIB_OUTSEGS,
  106.              tcp_skb_pcount(skb));
  107.     /*调用发送接口queue_xmit发送报文,进入到ip层,如果失败返回错误码。在TCP中该接口实现函数为ip_queue_xmit()*/
  108.     err = icsk->icsk_af_ops->queue_xmit(skb);
  109.     if (likely(err <= 0))
  110.         return err;

  111.     tcp_enter_cwr(sk, 1);

  112.     return net_xmit_eval(err);
  113. }

上一篇:linux内核中,tcp连接三次握手过程中的,tcp协议栈中的函数调用关系
下一篇:从编译器角度分析C语言中数组名和指针的区别