天天看点

tcp.c文件的tcp_send_skb函数1函数源码2函数用途3调用关系4语句注释

1函数源码

staticvoidtcp_send_skb(structsock *sk, struct sk_buff *skb)

{

       intsize;

       structtcphdr * th = skb->h.th;

       size = skb->len - ((unsignedchar *) th - skb->data);

       if (size < sizeof(structtcphdr) || size > skb->len)

       {

              printk("tcp_send_skb: bad skb (skb = %p, data = %p, th = %p, len = %lu)\n",

                     skb, skb->data, th, skb->len);

              kfree_skb(skb, FREE_WRITE);

              return;

       }

       if (size == sizeof(structtcphdr))

       {

              if(!th->syn && !th->fin)

              {

                     printk("tcp_send_skb: attempt to queue a bogon.\n");

                     kfree_skb(skb,FREE_WRITE);

                     return;

              }

       }

       tcp_statistics.TcpOutSegs++; 

       skb->h.seq = ntohl(th->seq) + size - 4*th->doff;

       if (after(skb->h.seq, sk->window_seq) ||

           (sk->retransmits && sk->ip_xmit_timeout == TIME_WRITE) ||

            sk->packets_out >= sk->cong_window)

       {

              th->check = 0;

              if (skb->next != NULL)

              {

                     printk("tcp_send_partial: next != NULL\n");

                     skb_unlink(skb);

              }

              skb_queue_tail(&sk->write_queue, skb);

              if (before(sk->window_seq, sk->write_queue.next->h.seq) &&

                  sk->send_head == NULL && sk->ack_backlog == 0)

                     reset_xmit_timer(sk, TIME_PROBE0, sk->rto);

       }

       else

       {

              th->ack_seq = ntohl(sk->acked_seq);

              th->window = ntohs(tcp_select_window(sk));

              tcp_send_check(th, sk->saddr, sk->daddr, size, sk);

              sk->sent_seq = sk->write_seq;

              sk->prot->queue_xmit(sk, skb->dev, skb, 0);

              reset_xmit_timer(sk, TIME_WRITE, sk->rto);

       }

}

2函数用途

发送数据并设置超时计时器。

3调用关系

4语句注释

4.1  size = skb->len - ((unsigned char *) th - skb->data);

skb->data:传输的数据包(包括所有头)。

skb->len:值等于MAC头长度 + Ip头长度 + tcp 头长度 + tcp层数据。

(unsigned char *) th - skb->data:tcp头的长度。(应该是mac头+ip头的长度吧?)

size:tcp头长度 + 负载数据。

4.2  tcp_statistics.TcpOutSegs++;  

       skb->h.seq = ntohl(th->seq) + size - 4*th->doff;

TcpOutSegs:TCP的发送数据包统计,tcp_statistics是struct tcp_mib类型。

skb->h.seq:存储对本数据包的确认序列号的值。

th->seq:tcp头的序列号域。

th->doff:tcp头的首部长度域,32bit为一个单位,即tcp头的长度为doff*4个字节长。

4.3 if (after(skb->h.seq, sk->window_seq) ||

          (sk->retransmits && sk->ip_xmit_timeout == TIME_WRITE) ||

          sk->packets_out >= sk->cong_window)

sk->window_seq:值为应答序列号加远端窗口大小。

sk->retransmits:重传次数。

sk->ip_xmit_timeout:计时器定时原因。

TIME_WRITE:宏值为1,重传类型的一种,即超时重传。

sk->packets_out:在已发送出去而尚未得到应答的数据包的个数。

sk->cong_window:拥塞窗口值。

4.4  if (before(sk->window_seq, sk->write_queue.next->h.seq) &&

          sk->send_head == NULL && sk->ack_backlog == 0)

              reset_xmit_timer(sk, TIME_PROBE0, sk->rto);

sk->send_head:该队列缓存已发送出去但尚未得到对方应答的数据包。

sk->ack_backlog:用于计算目前累计的应发送而未发送的应答数据包的个数。

sk->write_queue:该队列缓存所有由于窗口限制或者由于本地节制而缓存下来的数据包,该队列中可以无限制的缓存被节制下来的数据包。。

reset_xmit_timer:重新设置重传计时器。

TIME_PROBE0:非0窗口探测。

sk->rto:延迟时间值。

4.5  sk->sent_seq = sk->write_seq;

       sk->prot->queue_xmit(sk, skb->dev, skb, 0);

sk->sent_seq:表示本地将要发送的下一个数据包中第一个字节对应的序列号。

sk->write_seq:表示应用程序下一次写数据时所对应的第一个字节的序列号。write_seq 变量对应write_queue 写入队列;sent_seq 变量对应send_head, send_tail 表示的发送队列。内核将上层(网络层之上-即应用层)写入的数据和网络层向下层发送的数据区分对待。通常情况下,上层写入的数据会被网络层直接发往下层(网络层之下-即传输层))进行处理,换句话说,应用层写入的数据经过封装后将直接进入send_head,send_tail 表示的发送队列(不经过write_queue 写入队列),此时write_seq 和sent_seq 两个变量将始终相同,但是如果应用层写入的速度大于网络层(以及下层和网络传输介质)可以处理的速度,则数据需要先在write_queue进行缓存,此时write_seq 就大于sent_seq。write_seq 队列中数据包表示应用层写入的,但尚未发送出去的数据包;send_head,send_tail 表示的队列表示已经发送出去(此处发送出去并非一定是指已经发送到传输介质上,有可能数据包还缓存在硬件缓冲队列中,但一旦交给硬件,我们即认为已经发送出去)并等待ACK 的数据包,所以send_head,send_tail 表示的队列又称为重发队列。TCP 协议发生超时重发时,即从该队列中取数据包重新发送。

sk->prot->queue_xmit:指向ip_queue_xmit函数,功能是将数据发送出去,接着要启动一个超时计时器,以便在超时时重发此数据包。

转自:http://blog.chinaunix.net/space.php?uid=22066806&do=blog&id=1799358

继续阅读