理解struct msghdr
当我第一次看到他时,他看上去似乎是一个需要创建的巨大的结构。但是不要怕。其结构定义如下:
struct msghdr {
void *msg_name;
socklen_t msg_namelen;
struct iovec *msg_iov;
size_t msg_iovlen;
void *msg_control;
size_t msg_controllen;
int msg_flags;
};
结构成员可以分为四组。他们是:
套接口地址成员msg_name与msg_namelen。
I/O向量引用msg_iov与msg_iovlen。
附属数据缓冲区成员msg_control与msg_controllen。
接收信息标记位msg_flags。
在我们将这个结构分为上面的几类以后,结构看起来就不那样巨大了。
成员msg_name与msg_namelen
这些成员只有当我们的套接口是一个数据报套接口时才需要。msg_name成员指向我们要发送或是接收信息的套接口地址。成员msg_namelen指明了这个套接口地址的长度。
当调用recvmsg时,msg_name会指向一个将要接收的地址的接收区域。当调用sendmsg时,这会指向一个数据报将要发送到的目的地址。
注意,msg_name定义为一个(void *)数据类型。我们并不需要将我们的套接口地址转换为(struct sockaddr *)。
成员msg_iov与msg_iovlen
这些成员指定了我们的I/O向量数组的位置以及他包含多少项。msg_iov成员指向一个struct iovec数组。我们将会回忆起I/O向量指向我们的缓冲区。成员msg_iov指明了在我们的I/O向量数组中有多少元素。
成员msg_control与msg_controllen
这些成员指向了我们附属数据缓冲区并且表明了缓冲区大小。msg_control指向附属数据缓冲区,而msg_controllen指明了缓冲区大小。
成员msg_flags
当使用recvmsg时,这个成员用于接收特定的标记位(他并不用于sendmsg)。在这个位置可以接收的标记位如下表所示:
标记位 描述
MSG_EOR 当接收到记录结尾时会设置这一位。这通常对于SOCK_SEQPACKET套接口类型十分有用。
MSG_TRUNC 这个标记位表明数据的结尾被截短,因为接收缓冲区太小不足以接收全部的数据。
MSG_CTRUNC 这个标记位表明某些控制数据(附属数据)被截短,因为缓冲区太小。
MSG_OOB 这个标记位表明接收了带外数据。
MSG_ERRQUEUE 这个标记位表明没有接收到数据,但是返回一个扩展错误。
我们可以在recvmsg(2)与sendmsg(2)的man手册页中查看更多的信息。
附属数据结构与宏
recvmsg与sendmsg函数允许程序发送或是接收附属数据。然而,这些额外的信息受限于一定的格式规则。这一节将会介绍控制信息头与程序将会用来管理这些信息的宏。
简介struct cmsghdr结构
附
属信息可以包括0,1,或是更多的单独附属数据对象。在每一个对象之前都有一个struct
cmsghdr结构。头部之后是填充字节,然后是对象本身。最后,附属数据对象之后,下一个cmsghdr之前也许要有更多的填充字节。在这一章,我们将
要关注的附属数据对象是文件描述符与证书结构。
下图显示了一个包含附属数据的缓冲区是如何组织的。
我们需要注意以下几点:
cmsg_len与CMSG_LEN()宏值所显示的长度相同。
CMSG_SPACE()宏可以计算一个附属数据对象的所必需的空白。
msg_controllen是CMSG_SPACE()长度之后,并且为每一个附属数据对象进行计算。
控制信息头部本身由下面的C结构定义:
struct cmsghdr {
socklen_t cmsg_len;
int cmsg_level;
int cmsg_type;
/* u_char cmsg_data[]; */
};
其成员描述如下:
成员 描述
cmsg_len 附属数据的字节计数,这包含结构头的尺寸。这个值是由CMSG_LEN()宏计算的。
cmsg_level 这个值表明了原始的协议级别(例如,SOL_SOCKET)。
cmsg_type 这个值表明了控制信息类型(例如,SCM_RIGHTS)。
cmsg_data 这个成员并不实际存在。他用来指明实际的额外附属数据所在的位置。
这一章所用的例子程序只使用SOL_SOCKET的cmsg_level值。这一章我们感兴趣的控制信息类型如下(cmsg_level=SOL_SOCKET):
cmsg_level 描述
SCM_RIGHTS 附属数据对象是一个文件描述符
SCM_CREDENTIALS 附属数据对象是一个包含证书信息的结构
简介cmsg(3)宏
由于附属数据结构的复杂性,Linux系统提供了一系列的C宏来简化我们的工作。另外,这些宏可以在不同的UNIX平台之间进行移植,并且采取了一些措施来防止将来的改变。这些宏是由cmsg(3)的man手册页来进行描述的,其概要如下:
#include
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);
void *CMSG_DATA(struct cmsghdr *cmsg);
CMSG_LEN()宏
这个宏接受我们希望放置在附属数据缓冲区中的对象尺寸作为输入参数。如果我们回顾一个我们前面的介绍,我们就会发现这个宏会计算cmsghdr头结构加上所需要的填充字符的字节长度。这个值用来设置cmsghdr对象的cmsg_len成员。
下面的例子演示了如果附属数据是一个文件描述符,我们应如何来计算cmsg_len成员的值:
int fd; /* File descriptor */
printf("cmsg_len = %d/n",CMSG_LEN(sizeof fd));
CMSG_SPACE()宏
这个宏用来计算附属数据以及其头部所需的总空白。尽管CMSG_LEN()宏计算了一个相似的长度,CMSG_LEN()值并不包括可能的结尾的填充字符。CMSG_SPACE()宏对于确定所需的缓冲区尺寸是十分有用的,如下面的示例代码所示:
int fd; /* File Descriptor */
char abuf[CMSG_SPACE(sizeof fd)];
这个例子在abuf[]中声明了足够的缓冲区空间来存放头部,填充字节以及附属数据本身,和最后的填充字节。如果在缓冲区中有多个附属数据对象,一定要同时添加多个CMSG_SPACE()宏调用来得到所需的总空间。
CMSG_DATA()宏
这个宏接受一个指向cmsghdr结构的指针。返回的指针值指向跟随在头部以及填充字节之后的附属数据的第一个字节(如果存在)。如果指针mptr指向一个描述文件描述符的可用的附属数据信息头部,这个文件描述符可以用下面的代码来得到:
struct cmsgptr *mptr;
int fd; /* File Descriptor */
. . .
fd = *(int *)CMSG_DATA(mptr);
CMSG_ALIGN()宏
这是一个Linux扩展宏,而不是Posix.1g标准的一部分。指定一个字节长度作为输入,这个宏会计算一个新的长度,这个新长度包括为了维护对齐所需要的额外的填充字节。
CMSG_FIRSTHDR()宏
这
个宏用于返回一个指向附属数据缓冲区内的第一个附属对象的struct cmsghdr指针。输入值为是指向struct
msghdr结构的指针(不要与struct
cmsghdr相混淆)。这个宏会估计msghdr的成员msg_control与msg_controllen来确定在缓冲区中是否存在附属对象。然
后,他会计算返回的指针。
如果不存在附属数据对象则返回的指针值为NULL。否则,这个指针会指向存在的第一个struct cmsghdr。这个宏用在一个for循环的开始处,来开始在附属数据对象中遍历。
CMSG_NXTHDR()宏
这个用于返回下一个附属数据对象的struct cmsghdr指针。这个宏会接受两个输入参数:
指向struct msghdr结构的指针
指向当前struct cmsghdr的指针
如果没有下一个附属数据对象,这个宏就会返回NULL。
遍历附属数据
当接收到一个附属数据时,我们可以使用CMSG_FIRSTHDR()与CMSG_NXTHDR()宏来在附属数据对象中进行遍历。下面的示例代码显示了for循环的通常格式以及宏的相应用法:
struct msghdr msgh; /* Message Hdr */
struct cmsghdr *cmsg;0 /*Ptr to ancillary hdr */
int *fd_ptr; /* Ptr to file descript.*/
int received_fd; /* The file descriptor */
for ( cmsg=CMSG_FIRSTHDR(&msgh); cmsg!=NULL; cmsg=CMSG_NXTHDR(&msgh,cmsg) ) {
if ( cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS ) {
fd_ptr = (int *) CMSG_DATA(cmsg);
received_fd = *fd_ptr;
break;
}
}
if ( cmsg == NULL ) {
/* Error: No file descriptor recv‘d */
}
创建附属数据
要发送一个文件描述符的进程必须使用正确的格式化数据来创建一个附属数据缓冲区。下面的代码展示的通常的创建过程:
struct msghdr msg; /* Message header */
struct cmsghdr *cmsg; /* Ptr to ancillary hdr */
int fd; /* File descriptor to send */
char buf[CMSG_SPACE(sizeof fd)]; /* Anc. buf */
int *fd_ptr; /* Ptr to file descriptor */
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 fd);
/* Initialize the payload: */
fd_ptr = (int *)CMSG_DATA(cmsg);
*fd_ptr = fd;
/*
* Sum of the length of all control
* messages in the buffer:
*/
msg.msg_controllen = cmsg->cmsg_len;
struct msghdr和struct cmsghdr【转载】
时间: 2024-10-20 20:11:18
struct msghdr和struct cmsghdr【转载】的相关文章
struct msghdr和struct cmsghdr
struct msghdr和struct cmsghdr http://blog.csdn.net/wsllq334/article/details/6977039 structdescriptorcredentialsfilesocket数据结构 理解struct msghdr 当我第一次看到他时,他看上去似乎是一个需要创建的巨大的结构.但是不要怕.其结构定义如下: struct msghdr { void *msg_name; socklen_t msg_namelen; struct io
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
【转载】struct和typedef struct彻底明白了
分三块来讲述: 1 首先://注意在C和C++里不同 在C中定义一个结构体类型要用typedef: typedef struct Student { int a; }Stu; 于是在声明变量的时候就可:Stu stu1;(如果没有typedef就必须用struct Student stu1;来声明) 这里的Stu实际上就是struct Student的别名.Stu==struct Student 另外这里也可以不写Student(于是也不能struct Student stu1;了,必须是Stu
内核中用于数据接收的结构体struct msghdr(转)
内核中用于数据接收的结构体struct msghdr(转) 我们从一个实际的数据包发送的例子入手,来看看其发送的具体流程,以及过程中涉及到的相关数据结构.在我们的虚拟机上发送icmp回显请求包,ping另一台主机172.16.48.1.我们使用系统调用sendto发送这个icmp包. ssize_t sendto(int s, const void *buf, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
转载:struct和typedef struct彻底明白了
struct和typedef struct 分三块来讲述: 1 首先://注意在C和C++里不同 在C中定义一个结构体类型要用typedef: typedef struct Student { int a; }Stu; 于是在声明变量的时候就可:Stu stu1;(如果没有typedef就必须用struct Student stu1;来声明) 这里的Stu实际上就是struct Student的别名.Stu==struct Student 另外这里也可以不写Student(于是也不能struct
strcut的用法--------C语言结构体(struct)常见使用方法(转载)
今天复习一下struct,顺便挖掘一下以前没注意的小细节: 基本定义:结构体,通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,通过一定方法访问修改内部变量. 结构体定义: 第一种:只有结构体定义 [cpp] view plain copy struct stuff{ char job[20]; int age; float height; }; 第二种:附加该结构体类型的"结构体变量"的初始化的结构体定义 [cpp]
linux内核内存分配(二、struct slab和struct kmem_cache)
前一篇bloglinux内核内存分配(一.基本概念)主要是分析linux内核内存的分配和物理页分配函数接口.但是在实际的操作中,不一定所有内存申请都需要一个物理页,很多只是需要分配几K大小的内存就可以.所以就需要更小的内存分配函数.刚开始看这个有点不懂,不过懂了就很简单了.哈哈. slab思想 摘抄<深入linux设备驱动程序内核机制>的一段话:slab分配器的基本思想是,先利用页面分配器分配出单个或者一组连续的物理页面,然后在此基础上将整块页面分割成多个相等的小内存单元,以满足小内存空间分配
struct timeval 和 struct timespec
struct timeval { time_t tv_sec; suseconds_t tv_usec; }; 测试代码如下: #include <stdio.h> #include <sys/time.h> #include <time.h> int gettimeofday(struct timeval *tv, struct timezone *tz); int main(int argc,char * argv[]){ struct timeval tv; wh
获取网络接口信息——ioctl()函数与结构体struct ifreq、 struct ifconf
转载请注明出处:windeal专栏 Linux 下 可以使用ioctl()函数 以及 结构体 struct ifreq 结构体struct ifconf来获取网络接口的各种信息. ioctl 首先看ioctl()用法 ioctl()原型如下: #include <sys/ioctl.h> int ioctl(int fd, int request, ...); 参数: fd : 文件描述符 request: 表示要请求的信息.如IP地址.网络掩码等 ... : 后面的可变