udp_sendmsg函数分析

来源:互联网 发布:伊思和兰芝哪个好 知乎 编辑:程序博客网 时间:2024/06/05 05:48

声明:本人也是初学者,错误在所难免,希望大家发现后能指出,不胜感激。愿共同进步

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

482 {

//结构体的初始化,以及一些变量的定义

//将sk转为udp内部使用的类型指针,在TCP/IP中充斥了这样的转换。主要原因是因为对于上层来说,需要一个统一的类型,而到了底层的具体实现时,都会将上层抽象的数据类型,转为自己所需的类型。

483         struct inet_opt *inet = inet_sk(sk);

484         struct udp_opt *up = udp_sk(sk);

485         int ulen = len;

486         struct ipcm_cookie ipc;

rt变量被初始化为NULL,此处为bug,详情http://blog.csdn.net/abc78400123/article/details/6695161

487         struct rtable *rt = NULL;
488         int free = 0;

489         int connected = 0;

490         u32 daddr, faddr, saddr;

491         u16 dport;
492         u8  tos;

493         int err;

//cork传递给ip_append_data,用于指出是否应该使用缓冲区机制。msg_flags其值即为传入的参数flags。 MSG_MORE标志表明还将发送更多的参数

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

495 

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

496         if (len > 0xFFFF)

497                 return -EMSGSIZE;//Message too long

499         /* 
500          *      Check the flags.
501          */

//检查是否含有带外标志,如果有的话,返回操作不被支持

503         if (msg->msg_flags&MSG_OOB)     /* Mirror BSD error message compatibility */
504                 return -EOPNOTSUPP;//Operation not supported on transport endpoint

506         ipc.opt = NULL;

//linux udp协议允许多个udp数据包合并成一个发送出去,提高发送效率。

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

508         if (up->pending) {
509                 /*
510                  * There are pending frames.
511                  * The socket lock must be held while it's corked.

512                  */

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

513                 lock_sock(sk);
514                 if (likely(up->pending)) {
515                         if (unlikely(up->pending != AF_INET)) {
516                                 release_sock(sk);
517                                 return -EINVAL;

518                         }

//将数据发送出去

519                         goto do_append_data;

520                 }

//释放上锁的套接字时

521                 release_sock(sk);

522         }

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

523         ulen += sizeof(struct udphdr);
524 
525         /*
526          *      Get and verify the address. 

527          */

//检查struct msghdr结构的msg_name字段,确定目的地址是否合法。

struct msghdr结构详见http://blog.csdn.net/abc78400123/article/details/6695276

//判断目的地址是否为空

528         if (msg->msg_name) {

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

//?估计是判断强制转换后赋值是否成功?

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

531                         return -EINVAL;

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

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

536 

//如果上述语句执行顺利,则把上层应用程序的套接字地址赋给本函数。

537                 daddr = usin->sin_addr.s_addr;
538                 dport = usin->sin_port;
539                 if (dport == 0)
540                         return -EINVAL;

541         } else {

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

542                 if (sk->sk_state != TCP_ESTABLISHED)

543                         return -EDESTADDRREQ;

//接下来两句赋值语句,隐藏内部数据的写法,C的指针擅长描述这种结构,C语言的不带方法的类继承实现。这个函数是附带在sock结构后面的内部数据,inet_sk(sk)负责起出这个数据,只在内部使用

544                 daddr = inet->daddr;

545                 dport = inet->dport;
546                 /* Open fast path for connected socket.
547                    Route will not be used, if at least one option is set.
548                  */
549                 connected = 1;

550         }

//接下来的一句不明白,求指点

551         ipc.addr = inet->saddr;

//ipc.oif设置为socket bind的interface

553         ipc.oif = sk->sk_bound_dev_if;

//如果是控制报文,则调用ip_cmsg_send函数处理报文。从函数名字上看,好像是要发送cmsg,然而实际上却没有任何数据发送。

554         if (msg->msg_controllen) {
555                 err = ip_cmsg_send(msg, &ipc);
556                 if (err)

557                         return err;

//下面不明白,有空再想

558                 if (ipc.opt)
559                         free = 1;
560                 connected = 0;
561         }
562         if (!ipc.opt)
563                 ipc.opt = inet->opt;
564 
565         saddr = ipc.addr;
566         ipc.addr = faddr = daddr;
567 
568         if (ipc.opt && ipc.opt->srr) {
569                 if (!daddr)
570                         return -EINVAL;
571                 faddr = ipc.opt->faddr;
572                 connected = 0;
573         }

574         tos = RT_TOS(inet->tos);

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

575         if (sk->sk_localroute || (msg->msg_flags & MSG_DONTROUTE) || 
576             (ipc.opt && ipc.opt->is_strictroute)) {
577                 tos |= RTO_ONLINK;
578                 connected = 0;
579         }
580 
581         if (MULTICAST(daddr)) {
582                 if (!ipc.oif)
583                         ipc.oif = inet->mc_index;
584                 if (!saddr)
585                         saddr = inet->mc_addr;
586                 connected = 0;

587         }

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

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

589         if (connected)
590                 rt = (struct rtable*)sk_dst_check(sk, 0);
//如果rt为空,即无路由信息,则先用struct flowi结构记录查找路由表的索引信息,再调用 ip_route_output_flow函数查询路由表,得到路由信息。
592         if (rt == NULL) {
593                 struct flowi fl = { .oif = ipc.oif,
594                                     .nl_u = { .ip4_u =
595                                               { .daddr = faddr,
596                                                 .saddr = saddr,
597                                                 .tos = tos } },
598                                     .proto = IPPROTO_UDP,
599                                     .uli_u = { .ports =
600                                                { .sport = inet->sport,
601                                                  .dport = dport } } };
602                 err = ip_route_output_flow(&rt, &fl, sk, !(msg->msg_flags&MSG_DONTWAIT));
603                 if (err)
604                         goto out;
605 
606                 err = -EACCES;
607                 if ((rt->rt_flags & RTCF_BROADCAST) &&
608                     !sock_flag(sk, SOCK_BROADCAST))
609                         goto out;
610                 if (connected)
611                         sk_dst_set(sk, dst_clone(&rt->u.dst));
612         }
613 
614         if (msg->msg_flags&MSG_CONFIRM)
615                 goto do_confirm;
616 back_from_confirm:
617 
618         saddr = rt->rt_src;
619         if (!ipc.addr)
620                 daddr = ipc.addr = rt->rt_dst;
621 
622         lock_sock(sk);
623         if (unlikely(up->pending)) {
624                 /* The socket is already corked while preparing it. */
625                 /* ... which is an evident application bug. --ANK */
626                 release_sock(sk);
627 
628                 NETDEBUG(if (net_ratelimit()) printk(KERN_DEBUG "udp cork app bug 2


\n"));
629                 err = -EINVAL;
630                 goto out;
631         }
632         /*
633          *      Now cork the socket to pend data.
634          */
635         inet->cork.fl.fl4_dst = daddr;
636         inet->cork.fl.fl_ip_dport = dport;
637         inet->cork.fl.fl4_src = saddr;
638         inet->cork.fl.fl_ip_sport = inet->sport;
639         up->pending = AF_INET;
640 
641 do_append_data:

642         up->len += ulen;

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

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

647                 udp_flush_pending_frames(sk);

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

648         else if (!corkreq)
649                 err = udp_push_pending_frames(sk, up);
650         release_sock(sk);
651 
652 out:
653         ip_rt_put(rt);
654         if (free)
655                 kfree(ipc.opt);
656         if (!err) {
657                 UDP_INC_STATS_USER(UDP_MIB_OUTDATAGRAMS);
658                 return len;
659         }
660         return err;
661 
662 do_confirm:
663         dst_confirm(&rt->u.dst);
664         if (!(msg->msg_flags&MSG_PROBE) || len)
665                 goto back_from_confirm;
666         err = 0;
667         goto out;
668 }
669
原创粉丝点击