cmsg函数用法详解
cmsg函数简介
- 头文件包含
#include <sys/socket.h>
- 函数定义
struct cmsghdr *CMSG_FIRSTHDR(struct msghdr * msgh );
struct cmsghdr *CMSG_NXTHDR(struct msghdr * msgh ,
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 );
cmsg函数常见使用错误
- 编译错误
warning: implicit declaration of function ‘cmsg’ [-Wimplicit-function-declaration]
解决办法:包含头文件
#include <sys/socket.h>
cmsg函数详细描述
这些宏用于创建和访问不属于套接字有效负载的控制消息(也称为辅助数据)。此控制信息可能包括接收数据包的接口、各种很少使用的头字段、扩展的错误描述、一组文件描述符或UNIX凭据。例如,控制消息可用于发送额外的标头字段,如IP选项。辅助数据通过调用sendmsg (2)发送,通过调用recvmsg (2)接收,更多信息请参见它们的手册页。
辅助数据是带有附加数据的cmsghdr结构序列。有关可用的控制消息类型,请参见特定的协议手册页。每个套接字允许的最大辅助缓冲区大小可以使用/proc/sys/net/core/optmem_max ;来设置(请参阅socket (7)
cmsghdr结构定义如下:
struct cmsghdr {
size_t cmsg_len; /* Data byte count, including header
(type is socklen_t in POSIX) */
int cmsg_level; /* Originating protocol */
int cmsg_type; /* Protocol-specific type */
/* followed by
unsigned char cmsg_data[]; */
};
永远不应该直接访问cmsghdr结构的序列。相反,只使用以下宏:
- CMSG_FIRSTHDR ()返回一个指针,指向与传递的msghdr 相关联的辅助数据缓冲区中的第一个cmsghdr,如果缓冲区中没有足够的空间容纳cmsghdr,则返回NULL。
- CMSG_NXTHDR ()在传递的cmsghdr 之后返回下一个有效的cmsghdr,当缓冲区中没有足够的空间时,它返回NULL。当初始化一个将包含一系列cmsghdr结构的缓冲区时(例如,要与sendmsg (2))一起发送),该缓冲区应首先为零初始化,以确保CMSG_NXTHDR ()的正确操作
- CMSG_ALIGN ()给定一个长度,返回它,包括所需的对齐方式。这是一个常量表达式。
- CMSG_SPACE ()返回具有传递数据长度的有效负载的辅助元素所占的字节数。这是一个常量表达式。
- CMSG_DATA ()返回一个指向cmsghdr 的数据部分的指针,不能假定所返回的指针被适当地对齐以访问任意有效负载数据类型。应用程序不应将其强制转换为与有效负载匹配的指针类型,而应使用memcpy (3)将数据复制到适当声明的对象或从该对象复制数据。
- CMSG_LEN ()返回要存储在cmsghdr结构的cmsg_len成员中的值,同时考虑任何必要的对齐方式。它以数据长度作为参数。这是一个常量表达式。
要创建辅助数据,首先用控制消息缓冲区的长度初始化msghdr的msg_controllen成员。在msghdr上使用CMSG_FIRSTHDR ()获得第一个控制消息,在CMSG_NXTHDR ()获得所有后续消息。在每个控制消息中,初始化cmsg_len(CMSG_LEN ())其他cmsghdr报头字段,数据部分使用CMSG_DATA ()最后,msghdr0的msg_controllen字段应设置为缓冲区中所有控制消息长度的CMSG_SPACE ()之和。有关msghdr 的更多信息,请参见recvmsg (2)
cmsg函数其他说明
为了可移植性,应该只使用这里描述的宏来访问辅助数据。CMSG_ALIGN ()是一个Linux扩展,不应该在可移植程序中使用。
在Linux中,CMSG_LEN ()、CMSG_DATA ()和CMSG_ALIGN ()是常量表达式(假设它们的参数是常量),这意味着这些值可以用来声明全局变量的大小。然而,这可能不是可移植的。
cmsg函数使用举例
以下代码在接收到的辅助缓冲区中查找 IP_TTL 选项:
struct msghdr msgh;
struct cmsghdr *cmsg;
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) {
memcpy(&receive_ttl, CMSG_DATA(cmsg), sizeof(received_ttl));
break;
}
}
if (cmsg == NULL) {
/* Error: IP_TTL not enabled or small buffer or I/O error */
}
下面的代码使用SCM_RIGHTS :通过UNIX域套接字传递文件描述符数组
struct msghdr msg = { 0 };
struct cmsghdr *cmsg;
int myfds[NUM_FD]; /* Contains the file descriptors to pass */
char iobuf[1];
struct iovec io = {
.iov_base = iobuf,
.iov_len = sizeof(iobuf)
};
union { /* Ancillary data buffer, wrapped in a union
in order to ensure it is suitably aligned */
char buf[CMSG_SPACE(sizeof(myfds))];
struct cmsghdr align;
} u;
msg.msg_iov = &io;
msg.msg_iovlen = 1;
msg.msg_control = u.buf;
msg.msg_controllen = sizeof(u.buf);
cmsg = CMSG_FIRSTHDR(&msg);
cmsg\->cmsg_level = SOL_SOCKET;
cmsg\->cmsg_type = SCM_RIGHTS;
cmsg\->cmsg_len = CMSG_LEN(sizeof(myfds));
memcpy(CMSG_DATA(cmsg), myfds, sizeof(myfds));
评论区