UNIX网络编程读书笔记:recvmsg和sendmsg函数

这两个函数是最通用的I/O函数。实际上我们可以把所有read、readv、recv和recvfrom调用替换成recvmsg调用。类似地,各种输出函数调用也可以替换成sendmsg调用。

#include <sys/socket.h>
ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);
返回:读入或写出字节数——成功;-1——出错

这两个函数把大部分参数封装到一个msghdr结构中:

struct msghdr {
    void            *msg_name;           /* protocol address */
    socklen_t        msg_namelen;        /* size of protocol address */
    struct iovec    *msg_iov;            /* scatter/gather array */
    int              msg_iovlen;         /* # elements in msg_iov */
    void            *msg_control;        /* ancillary data (cmsghdr struct) */
    socklen_t        msg_controllen;     /* length of ancillary data */
    int              msg_flags;          /* flags returned by recvmsg() */
};

这里给出的msghdr结构符合POSIX规范。有些系统仍然使用本结构源自4.2BSD的较旧版本。这个较旧的结构没有msg_flags成员,而且msg_control和msg_controllen成员分别被称为msg_accrights和msg_accrightslen。这个较旧结构唯一支持的辅助数据形式用于传递文件描述字(称为访问权限)。

msg_name和msg_namelen这两个成员用于套接口未连接的场合(譬如未连接UDP套接口)。它们类似reacvfrom和sendto的第5和第6个参数:msg_name指向一个套接口地址结构,调用者在其中存放接收者(对于sendmsg调用)或发送者(对于recvmsg调用)的协议地址。如果无需指明协议地址(例如对于TCP套接口或已连接UDP套接口),msg_name应置为空指针。msg_namelen对于sendmsg是一个值参数,对于recvmsg却是一个值-结果参数。

msg_iov和msg_iovlen这两个成员指定输入或输出缓冲区数组(即iovec结构数组),类似readv和writev的第2和第3个参数。

msg_control和msg_controllen这两个成员指定可选的辅助数据的位置和大小。

对于recvmsg和sendmsg,我们必须区别它们的两个标志变量:一个是传递值的flags参数,另一个是所传递msghdr结构的msg_flags成员,它传递的是引用,因为传递给函数的是该结构的地址。

红心只有recvmsg使用msg_flags成员。recvmsg被调用时,flags参数被拷贝到msg_flags成员,并由内核使用其值驱动接收处理过程。内核还依据recvmsg的结构更新msg_flags成员的值。

红心sendmsg忽略msg_flags成员,因为它直接使用flags参数驱动发送处理过程。这一点意味着如果想在某个sendmsg调用中设置MSG_DONTWAIT标志,那就把flags参数设置为该值;把msg_flags成员设置为该值不起作用。

如下图所示,汇总了内核为相关输入和输出函数检查的flags参数值以及recvmsg可能返回的msg_flags成员值。其中没有sendmsg msg_flags一栏,因为本组合无效。

标志 内核检查:
send flags
sendto flags
sendmsg flags
内核检查:
recv flags
recvfrom flags
recvmsg flags
内核返回:
recvmsg msg_flags
MSG_DONTROUTE
MSG_DONTWAIT
MSG_PEEK
MSG_WAITALL
         太阳
         太阳


          太阳
          太阳
          太阳
 
MSG_EOR
MSG_OOB
         太阳
         太阳

          太阳
              太阳
              太阳
MSG_BCAST
MSG_MCAST
MSG_TRUNC
MSG_CTRUNC
MSG_NOTIFICATION
                  太阳
              太阳
              太阳
              太阳
              太阳

这些标志中,内核只检查而不返回前4个标志;既检查又返回下2个标志;不检查而只返回后5个标志。recvmsg返回的7个标志解释如下:

MSG_BCAST    本标志随BSD/OS引入,相对较新。它的返回条件是:本数据报作为链路层广播收取或者其宿IP地址是一个广播地址。

MSG_MCAST    本标志随BSD/OS引入,相对较新。它的返回条件是:本数据报作为链路层多播收取。

MSG_TRUNC    本标志的返回条件是:本数据报被截断;也就是说,内核预备返回的数据超过进程事先分配的空间(所有iov_len成员之和)。

MSG_CTRUNC  本标志的返回条件是:本数据报的辅助数据被截断;也就是说,内核预备返回的辅助数据超过进程事先分配的空间(msg_controllen)。

MSG_EOR        如果返回的数据不是一个逻辑记录的结尾所在,本标志将清零;否则本标志将设置。TCP不使用本标志,因为它是一个字节流协议。

MSG_OOB       本标志绝不为TCP带外数据返回。它用于其他协议族(例如OSI协议族)。

MSG_NOTIFICATION    本标志的返回条件是:SCTP接收端读入的本消息是一个事件通知,而不是一个数据消息。

如下图所示,展示了一个msghdr结构以及它指向的各种信息。图中假设进程即将对一个UDP套接口调用recvmsg。

image

图中给协议地址分配了16个字节,给辅助数据分配了20个字节。为缓冲数据初始化了3个iovec结构构成的数组:第一个指定一个100字节的缓冲区,第二个指定一个60字节的缓冲区,第三个指定一个80字节的缓冲区。我们还假设已为这个套接口设置了IP_RECVDSTADDR套接口选项,以接收所读取UDP数报的宿IP地址。

我们接着假设从192.6.38.100端口2000到达一个170字节的UDP数据报,它的目的地是我们的UDP套接口,宿IP地址为206.168.112.96。如下图所示,展示了recvmsg返回时msghdr结构中的所有信息。(图中被修改过的字段标了阴影)

image

红心如下图所示为5组I/O函数之间的差异:

image

posted @ 2014-03-18 11:40  ITtecman  阅读(7407)  评论(0编辑  收藏  举报