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

2011年(1)

2010年(5)

2009年(35)

我的朋友

分类: LINUX

2009-08-28 14:54:30

tcp.c文件的tcp_send_ack函数

978计划工作组 2009-8-28

1函数源码

/*

 *    This routine sends an ack and also updates the window.

 */

 

static void tcp_send_ack(unsigned long sequence, unsigned long ack,

            struct sock *sk,

            struct tcphdr *th, unsigned long daddr)

{

       struct sk_buff *buff;

       struct tcphdr *t1;

       struct device *dev = NULL;

       int tmp;

 

       if(sk->zapped)

              return;            /* We have been reset, we may not send again */

             

       /*

        * We need to grab some memory, and put together an ack,

        * and then put it into the queue to be sent.

        */

 

       buff = sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);

       if (buff == NULL)

       {

              /*

               *    Force it to send an ack. We don't have to do this

               *    (ACK is unreliable) but it's much better use of

               *    bandwidth on slow links to send a spare ack than

               *    resend packets.

               */

               

              sk->ack_backlog++;

              if (sk->ip_xmit_timeout != TIME_WRITE && tcp_connected(sk->state))

              {

                     reset_xmit_timer(sk, TIME_WRITE, HZ);

              }

              return;

       }

 

       /*

        *    Assemble a suitable TCP frame

        */

        

       buff->len = sizeof(struct tcphdr);

       buff->sk = sk;

       buff->localroute = sk->localroute;

       t1 =(struct tcphdr *) buff->data;

 

       /*

        *    Put in the IP header and routing stuff.

        */

        

       tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,

                            IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);

       if (tmp < 0)

       {

             buff->free = 1;

              sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);

              return;

       }

       buff->len += tmp;

       t1 =(struct tcphdr *)((char *)t1 +tmp);

 

       memcpy(t1, th, sizeof(*t1));

 

       /*

        *    Swap the send and the receive.

        */

        

       t1->dest = th->source;

       t1->source = th->dest;

       t1->seq = ntohl(sequence);

       t1->ack = 1;

       sk->window = tcp_select_window(sk);

       t1->window = ntohs(sk->window);

       t1->res1 = 0;

       t1->res2 = 0;

       t1->rst = 0;

       t1->urg = 0;

       t1->syn = 0;

       t1->psh = 0;

       t1->fin = 0;

      

       /*

        *    If we have nothing queued for transmit and the transmit timer

        *    is on we are just doing an ACK timeout and need to switch

        *    to a keepalive.

        */

        

       if (ack == sk->acked_seq)

       {

              sk->ack_backlog = 0;

              sk->bytes_rcv = 0;

              sk->ack_timed = 0;

              if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL

                              && sk->ip_xmit_timeout == TIME_WRITE)

              {

                     if(sk->keepopen) {

                            reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);

                     } else {

                            delete_timer(sk);

                     }

              }

      }

     

      /*

       *    Fill in the packet and send it

       */

       

      t1->ack_seq = ntohl(ack);

      t1->doff = sizeof(*t1)/4;

      tcp_send_check(t1, sk->saddr, daddr, sizeof(*t1), sk);

      if (sk->debug)

              printk("\rtcp_ack: seq %lx ack %lx\n", sequence, ack);

      tcp_statistics.TcpOutSegs++;

      sk->prot->queue_xmit(sk, dev, buff, 1);

}

2函数用途

向远端发送应答数据包并且更新窗口。

3调用关系

4语句注释

4.1  if(sk->zapped)

              return;        /* We have been reset, we may not send again */

sk->zapped标识本地是否接收到远端发送的RESET复位数据包,zapped=1表示对方发送了复位数据包进行了连接复位,所以在发送任何数据包(包括应答数据包)之前必须重新进行连接,换句话说,如果zapped=1,此处不必发送应答数据包。

4.2  buff = sk->prot->wmalloc(sk, MAX_ACK_SIZE, 1, GFP_ATOMIC);

MAX_ACK_SIZE:宏值为40 + MAX_HEADERMAX_HEADER宏值为18包括了MAC首部14个字节以及尾部冗余校验序列4 个字节40表示20字节IP首部加上20字节TCP首部。

GFP_ATOMIC:表示不能睡眠的内存分配,即使没有足够的连续内存块可供使用,内核可能无法释放出内存来,内核也不能让调用者睡眠

sk->prot->wmalloc:函数指针指向sock_wmalloc是发送数据包时内核缓冲区分配函数,其在分配之前,主要是检查当前发送缓冲区是否有足够剩余空闲空间分配指定大小的缓冲区。此处我们可以澄清一个概念,所谓发送缓冲区并非是指系统分配一段连续的指定大小的缓冲区,然后发送的数据都依次填充到这个区域被内核网络代码处理。实际上所谓发送缓冲区(接收缓冲区也如此)仅仅指定一个大小,至于具体的缓冲区空间则随时随地进行分配,换句话说,这些分配的缓冲区不存在连续的概念,内核所关心的只是这些分配的缓冲区大小的总和是否超过了指定的大小。一旦超出这个指定值,就表示缓冲区溢出,此时无法再分配缓冲区。即发送或者接收缓冲区只是一个大小上的限定,没有固定分配一段内存空间之说

buff:一个sk_buff的指针,指向分配的空间

1:表大于发送缓冲区依然要分配

4.3  sk->ack_backlog++;

       if (sk->ip_xmit_timeout != TIME_WRITE && tcp_connected(sk->state))

       {

              reset_xmit_timer(sk, TIME_WRITE, HZ);

       }

sk->ack_backlog目前累计的应发送而未发送的应答数据包的个数

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

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

tcp_connected用于检测连接是否被断开,监测的依据是只要有一方可以发送数据,连接就不算是断开的,即双方还处于连接

4.4 buff->localroute = sk->localroute;

       t1 =(struct tcphdr *) buff->data;

sk->localroute:此值为1表示是一个本地路由

t1因为buff->data是常指针类型,所以要通过t1data进行操作

4.5  tmp = sk->prot->build_header(buff, sk->saddr, daddr, &dev,

              IPPROTO_TCP, sk->opt, MAX_ACK_SIZE,sk->ip_tos,sk->ip_ttl);

build_header:此函数指针指向ip_build_header函数,调用此函数后sk->datamac头和ip头将被填充

devstruct device类型,存储网络设备信息,此处分配空间,被ip_build_header函数赋值

IPPPROTO_TCP:宏值为6,表使用tcp协议。

tmp:是ip_build_header的返回值,值为mac首部和ip首部的总长度,此值小于0表首部创建失败。

sk->optIP选项,本版本不支持

MAX_ACK_SIZEIP首部和IP负载总长度

sk->ip_tosIP首部的tos域,表服务类型

sk->ip_ttlIP首部的ttl域,表跳数

4.6  if (tmp < 0)

       {

            buff->free = 1;

              sk->prot->wfree(sk, buff->mem_addr, buff->mem_len);

              return;

       }

tmp<0:小于0表示创建头失败

buff->free是否提供可靠性传输保证的一个标志,0为可靠,1为不可靠。

sk->prot->wfree:此函数指针指向sock_wfree,功能是释放发送缓冲区

buff->mem_addrsk_buff 结构在内存中的基地址,该字段用于释放该sk_buff 结构。。

buff->mem_len表示sk_buff 结构大小加上数据帧(data域)的总长度。

4.7   if (ack == sk->acked_seq)

   {

      sk->ack_backlog = 0;

      sk->bytes_rcv = 0;

      sk->ack_timed = 0;

      if (sk->send_head == NULL && skb_peek(&sk->write_queue) == NULL

              && sk->ip_xmit_timeout == TIME_WRITE)

      {

         if(sk->keepopen) {

            reset_xmit_timer(sk,TIME_KEEPOPEN,TCP_TIMEOUT_LEN);

         } else {

            delete_timer(sk);

         }

      }

  }

ackTCP首部中ack_seq字段,即应答序列号,或者说希望远端发送的下一个字节数据的序列号

sk->acked_seq:表示该套接字希望从远端接收的下一个字节的序列号

ack==sk->acked_seq:如果二者相等,表示网络没有未响应的包了,所以此时可对应答数据包计数器清零

sk->ack_backlog应发送而未发送的应答数据包的个数。

sk->bytes_rcv:已接收字节总数

sk->ack_timed

sk->keepopen值为1表示使用保活定时器。

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