Sword 进程间传递打开的文件描述符(API介绍)

头文件

#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_contril中。 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 更新,不过没有标志返回给进程。

 

posted on 2023-02-25 13:35  寒魔影  阅读(51)  评论(0编辑  收藏  举报

导航