sendmsg/recvmsg和struct msghdr
函数原型
#include <sys/types.h> #include <sys/socket.h> ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
两函数都用到struct msghdr:
struct msghdr { void *msg_name; /* optional address */ socklen_t msg_namelen; /* size of address */ struct iovec *msg_iov; /* scatter/gather array */ size_t msg_iovlen; /* # elements in msg_iov */ void *msg_control; /* ancillary data, see below */ size_t msg_controllen; /* ancillary data buffer len */ int msg_flags; /* flags on received message */ };
msg_name and msg_namelen specify the source address if the socket is unconnected; msg_name may be given as a NULL pointer if no names are desired or required.
The fields msg_iov and msg_iovlen describe scatter-gather locations, as discussed in readv(2).
The field msg_control, which has length msg_controllen, points to a buffer for other protocol control-related messages or miscellaneous ancillary data. When recvmsg() is called, msg_controllen should contain the length of the available buffer in msg_control; upon return from a successful call it will contain the length of the control message sequence.
The msg_flags field in the msghdr is set on return of recvmsg(), MSG_EOR, MSG_TRUNC, MSG_CTRUNC, MSG_OOB, MSG_ERRQUEUE.
消息的格式定义为struct cmsghdr:
struct cmsghdr { socklen_t cmsg_len; /* data byte count, including hdr */ int cmsg_level; /* originating protocol */ int cmsg_type; /* protocol-specific type */ /* followed by unsigned char cmsg_data[]; */ };
控制消息的访问通过宏cmsg(3)实现:
#include <sys/socket.h> struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh); struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg); size_t CMSG_ALIGN(size_t length); size_t CMSG_SPACE(size_t length); size_t CMSG_LEN(size_t length); unsigned char *CMSG_DATA(struct cmsghdr *cmsg);
These macros are used to create and access control messages (also called ancillary data) that are not a part of the socket payload.
Ancillary data is a sequence of struct cmsghdr structures with appended data. This sequence should be accessed using only the macros described in this manual page and never directly.
创建消息:
To create ancillary data, first initialize the msg_controllen member of the msghdr with the length of the control message buffer.
Use CMSG_FIRSTHDR() on the msghdr to get the first control message and CMSG_NXTHDR() to get all sub‐sequent ones.
In each control message, initialize cmsg_len (with CMSG_LEN()), the other cmsghdr header fields, and the data portion using CMSG_DATA().
Finally, the msg_controllen field of the msghdr should be set to the sum of the CMSG_SPACE() of the length of all control messages in the buffer.
示例
This code looks for the IP_TTL option in a received ancillary buffer: struct msghdr msgh; struct cmsghdr *cmsg; int *ttlptr; int received_ttl; /* Receive auxiliary data in msgh */ for (cmsg = CMSG_FIRSTHDR(&msgh); cmsg != NULL; cmsg = CMSG_NXTHDR(&msgh,cmsg)) { if (cmsg->cmsg_level == IPPROTO_IP && cmsg->cmsg_type == IP_TTL) { ttlptr = (int *) CMSG_DATA(cmsg); received_ttl = *ttlptr; break; } } if (cmsg == NULL) { /* * Error: IP_TTL not enabled or small buffer * or I/O error. */ }
The code below passes an array of file descriptors over a UNIX domain socket using SCM_RIGHTS:
struct msghdr msg = {0}; struct cmsghdr *cmsg; int myfds[NUM_FD]; /* Contains the file descriptors to pass. */ char buf[CMSG_SPACE(sizeof myfds)]; /* ancillary data buffer */ int *fdptr; msg.msg_control = buf; msg.msg_controllen = sizeof buf; cmsg = CMSG_FIRSTHDR(&msg); cmsg->cmsg_level = SOL_SOCKET; cmsg->cmsg_type = SCM_RIGHTS; cmsg->cmsg_len = CMSG_LEN(sizeof(int) * NUM_FD); /* Initialize the payload: */ fdptr = (int *) CMSG_DATA(cmsg); memcpy(fdptr, myfds, NUM_FD * sizeof(int)); /* Sum of the length of all control messages in the buffer: */ msg.msg_controllen = cmsg->cmsg_len;