头文件 #include <sys/socket.h> 函数 int socketpair(int domain, int type, int protocol, int socket_vector[2]); 函数建立一对匿名的已经连接的套接字,其特性由协议族domain、类型type、协议protocol决定,建立的两个套接字描述符会放在socket_vector[0]和socket_vector[1]中。 参数说明 domain: 表示协议族,只能为 AF_LOCAL 或者 AF_UNIX ; type: 表示协议,可以是SOCK_STREAM、SOCK_DGRAM、SOCK_SEQPACKET; protocol: 表示类型,一般为0; socket_vector: socket_vector[2]是接收代表两个套接口的整数数组。每一个文件描述符代表一个套接口,并且与另一个并没有区别。 返回值说明 成功执行时,返回0。失败返回-1,errno被设为以下的某个值 备注 socketpair接口设计的思考 原来我认为父进程通过 socket_vector[0] 写数据,子进程在 socket_vector[0] 读数据,这个理解是错误的,实际上是父进程通过socket_vector[0] 写数据A,而子进程在 socket_vector[1] 读数据A 这产生第一个问题,socket是如何通信的呢? 当建立一个socket的时候,服务端会accept一个fd1,客户端会connect一个fd2,fd1和fd2之间进行通信,fd1写数据只能在fd2去读数据,fd2写数据只能在fd1去读数据。 那么fd1写数据是否可以在fd1去读呢? 实践证明不行,fd1无法读自己写的数据。那么我们通常写文件,只有一个fd,写文件或者网络通信写数据都可以通过write函数来实现, 猜测write函数中对于文件fd和网络fd的处理是不同的,文件fd写可能是向磁盘发送命令,网络fd可能是向网卡发送命令。 Linux只是对一些操作抽象为文件,但是各个文件应该还是有类型的。
头文件 #include <sys/socket.h> 函数 ssize_t sendmsg(int socket, const struct msghdr *message, int flags); 用于通过socket发送数据 参数说明 socket: 套接字 message: 数据 flags: 默认值0即可 返回值说明 成功返回发送的字节数;失败返回-1
头文件 #include <sys/socket.h> 函数 ssize_t recvmsg(int socket, struct msghdr *message, int flags); 用于通过socket发送数据 参数说明 socket: 套接字 message: 数据 flags: 默认值0即可 返回值说明 成功返回接收的字节数;失败返回-1
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 如果套接字是在未连接的场合进行传输数据(例如未连接UDP套接字), sendmsg函数: msg_name成员设置的是数据的接受者的地址,msg_namelen代表地址的长度 recvmsg函数: msg_name成员存放的是数据的发送者的地址,msg_namelen代表地址的长度 如果套接字是在已连接的场合进行传输数据(例如TCP通信/已连接UDP套接字),msg_name值为空指针,msg_namelen为0 msg_iov,msg_iovlen struct iovec { void * iov_base; size_t iov_len; }; msg_iov:用于发送/接受数据的缓冲区数组,可以用于发送/接受多组数据。每个成员是一个struct iovec类型的结构体 msg_iovlen:缓冲区数组元素个数 强调: 缓冲区是为msg_control准备的内存空间,不允许在CMSG_DATA操作之后对msg_iov[n]赋值,这会破坏已经存储的文件描述符 msg_control,msg_controllen msg_control:用于设置/存放辅助数据。辅助数据的数据类型为struct cmsghdr。以struct cmsghdr结构体指针开头,可以为多组 msg_controllen:辅助数据的总大小/长度 msg_flags 只有recvmsg函数才使用msg_flags成员。当调用recvmsg函数时,recvmsg函数的flags参数被复制到msg_flags上,并由内核使用其值驱动接受处理过程 sendmsg函数不设置此参数。因为它直接使用sendmsg函数的flags参数驱动发送处理过程。(如果msg_flags设置了值,则被sendmsg函数忽略。例如flags设置了MSG_DONTWAIT,msg_flags也设置了该值,则msg_flags的被忽略)
struct cmsghdr { size_t cmsg_len; 辅助数据的总长度,由 CMSG_LEN 宏直接获取 int cmsg_level; 表示通道使用的的原始协议级别,与 setsockopt 函数的 level 参数相同 int cmsg_type; 控制信息类型,例如,SCM_RIGHTS,辅助数据是文件描述符;SCM_CREDENTIALS,辅助数据是一个包含证书信息的结构 /* followed by unsigned char cmsg_data[]; */被注释的 cmsg_data 指明了物理内存中真正辅助数据的位置,帮助理解 }; #include <sys/socket.h> #include <sys/param.h> #include <sys/socket.h> size_t CMSG_LEN(size_t length); void* CMSG_DATA(struct cmsghdr *cmsg); CMSG_FIRSTHDR() 宏: 返回一个指针,该指针指向与传递的msghdr关联的辅助数据缓冲区中的第一个cmsghdr。如果缓冲区中没有足够的空间可容纳cmsghdr,则返回NULL。 CMSG_NXTHDR() 宏: 返回传递的cmsghdr之后的下一个有效cmsghdr。当缓冲区中没有足够的空间时,它将返回NULL。 初始化将包含一系列cmsghdr结构的缓冲区时(例如,将通过sendmsg(2)发送),该缓冲区应首先初始化为零以确保CMSG_NXTHDR()的正确操作。 CMSG_ALIGN() 宏: 给定长度,CMSG_ALIGN()将返回它,包括所需的对齐方式。这是一个常量表达式。 CMSG_SPACE() 宏: CMSG_SPACE()返回带有已传递数据长度的有效负载的辅助元素的字节数。这是一个常量表达式。 CMSG_DATA() 宏: CMSG_DATA()返回一个指向cmsghdr的数据部分的指针。返回的指针不能被假定为适当对齐以访问任意有效载荷数据类型。应用程序不应将其强制转换为与有效负载匹配的指针类型,而应使用memcpy(3)将数据复制到适当声明的对象或从适当声明的对象复制数据。 CMSG_LEN() 宏: CMSG_LEN()返回值,该值将存储在cmsghdr结构的cmsg_len成员中,同时考虑到任何必要的对齐方式。它以数据长度为参数。这是一个常量表达式。
图中被 recvmsg 修改过的字段标上了阴影。从第一幅图到第二幅图的变动包括以下几点:
a.由 msg_name 成员指向的缓冲区被填以一个网际网套接字地址结构,其中有所收到数据报的源 IP 地址和源 UPD 端口号。
b.msg_namelen 成员(一个值-结果参数)被更新为存放在 msg_name 所指缓冲区中的数据量。本成员并无变化,因为 recvmsg 调用前和返回后其值均为 16.
c.所收取数据报的前 100 个字节数据存放在第一个缓冲区,中 60 字节数据存放在第二个缓冲区,后 10 字节数据存放在第三个缓冲区。最后那个缓冲区的后 70 字节没有改动。recvmsg 函数的返回值(即 170)就是该数据报的大小。
d.由 msg_control 成员指向的缓冲区被填以一个 cmsghdr 结构。该 cmsghdr 结构中,cmsg_len 成员值为 16,cmsg_level 成员值为 IPPROTO_IP,cmsg_type 成员值为 IP_RECVDSTADDR,随后 4 个字节存放所收到 UDP 数据报的目的 IP 地址。这个 20 字节缓冲区的后 4 个字节没有改动。
e.msg_controllen 成员被更新为所存放辅助数据的实际数据量。本成员也是一个值-结果参数,recvmsg 返回时其结果为 16。
f.msg_flags 成员同样被 recvmsg 更新,不过没有标志返回给进程。
标签:socket,缓冲区,描述符,API,flags,Sword,msg,数据,CMSG From: https://www.cnblogs.com/zhanggaofeng/p/17154262.html