Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1742820
  • 博文数量: 1493
  • 博客积分: 38
  • 博客等级: 民兵
  • 技术积分: 5834
  • 用 户 组: 普通用户
  • 注册时间: 2009-08-19 17:28
文章分类

全部博文(1493)

文章存档

2016年(11)

2015年(38)

2014年(137)

2013年(253)

2012年(1054)

2011年(1)

分类:

2012-10-17 08:41:10

当调用sendto或其他应用层发送函数,向SOCK_DGRAM类套接字写数据,将引发内核调用udp_sendmsg函数,下面是阅读2.6内核相应部分后的一些笔记:

// 参数iocb是和内核I/O依赖的数据结构

int udp_sendmsg(struct kiocb *iocb, struct sock *sk, struct msghdr *msg,  size_t len)

{

         struct inet_opt *inet = inet_sk(sk); // 将sk强制转化成struct inet_sock类型

         struct udp_opt *up = udp_sk(sk); //将sk强制转化成struct udp_sock类型

         int ulen = len;

         struct ipcm_cookie ipc;

         struct rtable *rt = NULL;

         int free = 0;

         int connected = 0;

         u32 daddr, faddr, saddr;

         u16 dport;

         u8  tos;

         int err;

//cork传递给ip_append_data,用于指出是否应该使用缓冲区机制。

//msg_flags其值即为传入的参数flags。

// MSG_MORE标志表明还将发送更多的参数

         int corkreq = up->corkflag || msg->msg_flags&MSG_MORE;

//检查产度是否超过最大地址长度 

         if (len > 0xFFFF)

                 return -EMSGSIZE;

// MSG_OOB用来接受外带数据

         if (msg->msg_flags&MSG_OOB)    

                  return    -EOPNOTSUPP;

         ipc.opt = NULL;

//判断是否有数据需要发送。 如果该socket有等待发送的数据,那么直接将数据追加。如果没有就ulen加上udp首部的长度。

         if (up->pending) {

           //调用lock_sock函数锁定该套接字

                lock_sock(sk);

           //if(likely(value))和if(unlikely(value))都是宏函数,都是将"分支"信息提供给编译器以对代码进行优化,提高执行效率,带likely里的代码执行的机会更大些。

                 if (likely(up->pending)) {
                         if (unlikely(up->pending != AF_INET)) {
                                 release_sock(sk);

                                 return -EINVAL;

                         }

                         goto do_append_data;

                 }

                           release_sock(sk);

         }

         //ulen加上udp首部的长度。

         ulen = sizeof(struct udphdr);
        

         //msg-> msg_name  套接字名字

         if (msg->msg_name) {

                struct sockaddr_in * usin = (struct sockaddr_in*)msg->msg_name;

                 if (msg->msg_namelen < sizeof(*usin))

                         return -EINVAL;

//因为这是udp_sendmsg函数,当然使用TCP/IP协议,所以判断协议是否为AF_INET协议族,如果不是,继续判断是否为AF_UNSPEC,如果不是,则返回不可用的地址

                 if (usin->sin_family != AF_INET) {
                         if (usin->sin_family != AF_UNSPEC)
                                 return -EINVAL;
                 }

                  daddr = usin->sin_addr.s_addr;

             dport = usin->sin_port;

             if (dport == 0)

                         return -EINVAL;

         } else {

//即使目的地址为空,但如果套接字处于连接状态,则仍然认为目的地址合法,允许继续传送数据

                 if (sk->sk_state != TCP_ESTABLISHED)

                         return -EDESTADDRREQ;

                 daddr = inet->daddr;

                 dport = inet->dport;
               
                 connected = 1;

         }

         ipc.addr = inet->saddr;

         ipc.oif = sk->sk_bound_dev_if;

         //如果是控制报文,通过ip_cmsg_send处理控制报文

         if (msg->msg_controllen) {

                 err = ip_cmsg_send(msg, &ipc);
 
                if (err)

                         return err;

                 if (ipc.opt)

                        free = 1
            connected = 0;
         }
         if (!ipc.opt)
                 ipc.opt = inet->opt;
 
         saddr = ipc.addr;
         ipc.addr = faddr = daddr;
 
         if (ipc.opt && ipc.opt->srr) {
                 if (!daddr)
                         return -EINVAL;
                 faddr = ipc.opt->faddr;
                 connected = 0;
         }

         tos = RT_TOS(inet->tos);

        //确定是否需要路由信息

         if (sk->sk_localroute || (msg->msg_flags & MSG_DONTROUTE) || 
             (ipc.opt && ipc.opt->is_strictroute)) {
                 tos |= RTO_ONLINK;
                 connected = 0;
         }
 
         if (MULTICAST(daddr)) {
                 if (!ipc.oif)
                         ipc.oif = inet->mc_index;
                 if (!saddr)
                         saddr = inet->mc_addr;
                 connected = 0;

         }


//如果已经建立了套接字连接,则不需要重新查询路由

//直接从套接字的管理结构中返回路由表信息 ,并记录到rt 中

         if (connected)
                 rt = (struct rtable*)sk_dst_check(sk, 0);
//如果rt为空,即无路由信息,则先用struct flowi结构记录查找路由表的索引信息,再调用 ip_route_output_flow函数查询路由表,得到路由信息。
         if (rt == NULL) {
                 struct flowi fl = { .oif = ipc.oif,
                                            .nl_u = { .ip4_u =
                                               { .daddr = faddr,
                                                 .saddr = saddr,
                                                 .tos = tos } },
                                     .proto = IPPROTO_UDP,
                                     .uli_u = { .ports =
                                                { .sport = inet->sport,
                                                  .dport = dport } } };
                err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
                 if (err)
                         goto out;
 
                 err = -EACCES;
                 if ((rt->rt_flags & RTCF_BROADCAST) &&
                     !sock_flag(sk, SOCK_BROADCAST))
                         goto out;
                 if (connected)
                         sk_dst_set(sk, dst_clone(&rt->u.dst));
         }
 
         if (msg->msg_flags&MSG_CONFIRM)
                 goto do_confirm;
  back_from_confirm:
 
         saddr = rt->rt_src;
         if (!ipc.addr)
                 daddr = ipc.addr = rt->rt_dst;
 
         lock_sock(sk);
         if (unlikely(up->pending)) {
          
                 release_sock(sk);
 
                 err = -EINVAL;
                 goto out;
         }
        
         inet->cork.fl.fl4_dst = daddr;
         inet->cork.fl.fl_ip_dport = dport;
         inet->cork.fl.fl4_src = saddr;
         inet->cork.fl.fl_ip_sport = inet->sport;
         up->pending = AF_INET;
 
  do_append_data:

         up->len = ulen;

//对UDP数据包进行分片处理,为IP层分片处理做好准备

         err = ip_append_data(sk, ip_generic_getfrag, msg->msg_iov, ulen, 
                         sizeof(struct udphdr), &ipc, rt, 
                         corkreq ? msg->msg_flags|MSG_MORE : msg->msg_flags);
         if (err)

                 udp_flush_pending_frames(sk);

//上层应用指定flags为MSG_MORE时,corkreq为1

         else if (!corkreq)
                 err = udp_push_pending_frames(sk, up);
         release_sock(sk);
 
  out:
         ip_rt_put(rt);
         if (free)
                 kfree(ipc.opt);
         if (!err) {
                 UDP_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
                 return len;
         }
         return err;
 
  do_confirm:
         dst_confirm(&rt->u.dst);
         if (!(msg->msg_flags&MSG_PROBE) || len)
                 goto back_from_confirm;
         err = 0;
         goto out;
  }
阅读(583) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~