Chinaunix首页 | 论坛 | 博客
  • 博客访问: 155908
  • 博文数量: 41
  • 博客积分: 2500
  • 博客等级: 少校
  • 技术积分: 425
  • 用 户 组: 普通用户
  • 注册时间: 2009-07-14 10:16
文章分类
文章存档

2011年(1)

2010年(5)

2009年(35)

我的朋友

分类: LINUX

2009-08-06 17:50:39

tcp.c文件的tcp_do_retransmit函数

978计划工作组 2009-8-6

1函数源码

/*

 *    A socket has timed out on its send queue and wants to do a

 *    little retransmitting. Currently this means TCP.

 */

 

void tcp_do_retransmit(struct sock *sk, int all)

{

       struct sk_buff * skb;

       struct proto *prot;

       struct device *dev;

       int ct=0;

 

       prot = sk->prot;

       skb = sk->send_head;

 

       while (skb != NULL)

       {

              struct tcphdr *th;

              struct iphdr *iph;

              int size;

 

              dev = skb->dev;

              IS_SKB(skb);

              skb->when = jiffies;

 

              /*

               * In general it's OK just to use the old packet.  However we

               * need to use the current ack and window fields.  Urg and

               * urg_ptr could possibly stand to be updated as well, but we

               * don't keep the necessary data.  That shouldn't be a problem,

               * if the other end is doing the right thing.  Since we're

               * changing the packet, we have to issue a new IP identifier.

               */

 

              iph = (struct iphdr *)(skb->data + dev->hard_header_len);

              th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2));

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

             

              /*

               *    Note: We ought to check for window limits here but

               *    currently this is done (less efficiently) elsewhere.

               *    We do need to check for a route change but can't handle

               *    that until we have the new 1.3.x buffers in.

               *

               */

 

              iph->id = htons(ip_id_count++);

              ip_send_check(iph);

 

              /*

               *    This is not the right way to handle this. We have to

               *    issue an up to date window and ack report with this

               *    retransmit to keep the odd buggy tcp that relies on

               *    the fact BSD does this happy.

               *    We don't however need to recalculate the entire

               *    checksum, so someone wanting a small problem to play

               *    with might like to implement RFC1141/RFC1624 and speed

               *    this up by avoiding a full checksum.

               */

               

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

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

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

             

              /*

               *    If the interface is (still) up and running, kick it.

               */

 

              if (dev->flags & IFF_UP)

              {

                     /*

                      *    If the packet is still being sent by the device/protocol

                      *    below then don't retransmit. This is both needed, and good -

                      *    especially with connected mode AX.25 where it stops resends

                      *    occurring of an as yet unsent anyway frame!

                      *    We still add up the counts as the round trip time wants

                      *    adjusting.

                      */

                     if (sk && !skb_device_locked(skb))

                     {

                            /* Remove it from any existing driver queue first! */

                            skb_unlink(skb);

                            /* Now queue it */

                            ip_statistics.IpOutRequests++;

                            dev_queue_xmit(skb, dev, sk->priority);

                     }

              }

 

              /*

               *    Count retransmissions

               */

               

              ct++;

              sk->prot->retransmits ++;

 

              /*

               *    Only one retransmit requested.

               */

      

              if (!all)

                     break;

 

              /*

               *    This should cut it off before we send too many packets.

               */

 

              if (ct >= sk->cong_window)

                     break;

              skb = skb->link3;

       }

}

2函数用途

用于TCP 协议数据包重传,该函数遍历相应套接字重传队列,依照要求重传该队列中数据包。

3语句注释

3.1 prot = sk->prot;

       skb = sk->send_head;

sk->prot指向传输层的处理函数集。

sk->send_headtcp协议重发队列,指向队列的头,sk->send_tail指向同一个队列的尾,存储数据包已经发送出去了但未得到应答的数据报。是双向非循环链表。

3.2dev = skb->dev;

              IS_SKB(skb);

              skb->when = jiffies;

skb->dev:此数据结构存储网络设备的相关信息。

IS_SKB:是一个宏函数,宏展开后是调用skb_check函数,此函数的功能是检查sk_buff的合理性并打印相关信息。

skb->when:该数据包的发送时间。该字段用于计算往返时间RTT

jiffies:是记录着从电脑开机到现在总共的时钟中断次数。在linux内核中jiffies远比xtime重要,那么他取决于系统的频率,单位是Hz,这里不得不说一下频率的单位,1MHz1000000Hz6个零),1KHz=1000Hz3个零).频率是周期的倒数,一般是一秒钟中断产生的次数,所以,假如我们需要知道系统的精确的时间单位时,需要换算了,假如我们系统的频率是200Mhz,那么一次中断的间隔是1/ 200,000000Hz = 0.000 000 005秒看一下上面我们的时间单位,对照一下小数点后面是9个零,所以理论上我们系统的精确度是5纳秒。LINUX系统时钟频率是一个常数HZ来决定的,通常HZ100,那么他的精度就是10ms(毫秒)。也就是说每10ms一次中断。所以一般来说Linux的精确度是10毫秒。也就是x8610ms等于1滴答。

3.3 iph = (struct iphdr *)(skb->data + dev->hard_header_len);

       th = (struct tcphdr *)(((char *)iph) + (iph->ihl << 2));

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

dev->hard_header_len链路层的头长度。

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

skb->data + dev->hard_header_len指针移动到ip头位置。

iph指向ip头的位置。

iph->ihl<<2iph->ihlip头的首部长度域,在ip头结构中占4bit,首部长度指的是IP层头部(包括选项域)占32 bit字的数目或者说有多少个字(32位机)。<<2表示左移2位相当于乘以4,所以iph->ihl<<2表示以字节为单位的首部长度。

th指向tcp头的位置。

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

size值等于tcp 头长度 + tcp层数据。

3.4  iph->id = htons(ip_id_count++);

       ip_send_check(iph);

iphip头数据结构。

ip_id_countid计数器。

iph->id16位的标识字段,顺序递增。

Ip_send_check根据ip头计算验证和码并将结果赋给iph->check16位的首部校验和域)。计算数据报的IP检验和方法:首先把检验和字段置为0。然后对首部中每个16 bit进行二进制反码求和(整个首部看成是由一串16 bit的字组成),结果存在检验和字段中。此处注意求和产生的进位要看作一个新的16bit加到上一步的求和结果中。

当收到一份IP数据报后,同样对首部中每个16 bit进行二进制反码的求和。由于接收方在计算过程中包含了发送方存在首部中的检验和,因此如果首部在传输过程中没有发生任何差错,那么接收方计算的结果应该为全1。如果结果不是全1(即检验和错误),那么IP就丢弃收到的数据报。但是不生成差错报文,由上层去发现丢失的数据报并进行重传。

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

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

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

th->ack_seqtcp头数据结构的32位确认号域,按字节数递增。

sk->acked_seq本地希望从远端接收的下一个字节的序列号。

th->windowtcp头数据结构的16位窗口大小域,以字节为单位。

tcp_select_window:得到窗口大小,详情请参见《tcp.c文件的tcp_select_window函数(1)》。

sk->saddr本地地址。

sk->daddr远端地址。

tcp_send_check计算校验和并将结果赋给th->check16tcp校验和域)。

3.6 If (dev->flags & IFF_UP)

dev->flags设备状态,未启用则无法工作。

IFF_UP设备已启用。

3.7  if (sk && !skb_device_locked(skb))

       {

              skb_unlink(skb);

              ip_statistics.IpOutRequests++;

              dev_queue_xmit(skb, dev, sk->priority);

       }

skb_device_locked判读当前skb节点是否被锁定。

skb_unlink删除当前的skb节点。

ip_statisticsip_mib结构,是对ip协议相关信息的统计。

IpOutRequests设备高层发出的包数量。

sk->priority发送优先级。

dev此数据结构存储网络设备的相关信息

dev_queue_xmit链路层函数,实现发送skb的当前节点信息。

3.8 ct++;

   sk->prot->retransmits ++;

ct重发数据包的个数。

sk->prot->retransmits本数据包重传的次数。

3.9  if (!all)

              break;

all值为0,则表示只发送一个数据包后即可退出。

3.10 if (ct >= sk->cong_window)

              break;

       skb = skb->link3;

sk->cong_window拥塞窗口大小, 表示的是本地最大可同时发送但未得到应答的数据字节个数。

skb = skb->link3 重发链表,是一个单向非循环链表。

 

阅读(1423) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~