Linux高级进程间通信:UNIX域套接字

时间:2021-07-07 03:56:45

UNIX域套接字简介

UNIX域套接字用于在同一台机器上运行的进程间的通信。虽然因特网域套接字可用于同一目的,但UNIX域套接字的效率更高。UNIX域套接字仅仅复制数据,它们并不执行协议处理,不需要添加和删除网络报头,无需计算检验和,不要产生顺序号,无需发送确认报文。 UNIX域套接字提供流和数据报两种接口。UNIX域数据报服务是可靠的,既不会丢失消息也不会传递错误。UNIX域套接字是套接字和管道之间的混合物。为了创建一对非命名的、相互连接的UNIX域套接字,用户可以使用它们面向网络的域套接字接口,也可以使用socketpair函数。
#include <sys/types.h>          
#include <sys/socket.h>

int socketpair(int domain, int type, int protocol, int sockfd[2]);

注意:这里创建的非命名管道是一个全双工管道,即两个描述符分别既可以用于读,也可以用于写。如下是该函数使用的一个实例:
int fd[2];
pid_t pid;
socketpair(AF_UNIX, SOCKET_STREAM, 0, fd);// 创建管道
if ( (pid = fork()) == 0) {// 子进程
close(fd[0]);// 关闭管道的父进程端
dup2(fd[1], STDOUT_FILENO);// 复制管道的子进程端到标准输出
dup2(fd[1], STDIN_FILENO);// 复制管道的子进程端到标准输入
close(fd[1]);// 关闭已复制的读管道
/* 使用exec执行命令 */
} else {// 父进程
close(fd[1]);// 关闭管道的子进程端
/* 现在可在fd[0]中读写数据 */
shutdown(fd[0], SHUT_WR);// 通知对端数据发送完毕
/* 读取剩余数据 */
close(fd[0]);// 关闭管道
/* 使用wait系列函数等待子进程退出并取得退出代码 */
}

传送文件描述符

UNIX域套接字一个非常重要的用途就是用于进程间传输文件描述符。在进程间传输打开的文件描述符的能力是非常有用的,可以用它对客户端/服务器进程应用进行不同的设计。它使一个进程(一般是服务器进程)能够处理为打开一个文件所要求的一切操作以及向调用进程送回一个描述符,该描述符可被用于以后的所有I/O函数。涉及打开文件或设备的所有细节对客户端进程而言都是隐藏了的。 当一个进程向另一个进程传送一打开的文件描述符时,实际是想要发送进程和接受进程共享同一个文件表项。在技术上,发送进程实际上向接受进程传送一个指向一打开文件表项的指针。该指针被分配存放在接受进程的第一个可用描述符项中。(注意,不要造成错觉,以为发送进程和接受进程中的描述符编号是相同的,通常它们是不同的)。两个进程共享同一个打开文件表项,在这一点上与fork之后,父、子进程共享打开文件表项的情况完全相同。 当发送进程将描述符传送给接受进程后,通常它关闭该描述符。发送进程关闭该描述符并不造成关闭该文件或设备,其原因是该描述符对应的文件仍被视为由接受进程打开(即使接受进程尚未接受到该描述符)。 为了用UNIX域套接字交换文件描述符,调用sendmsg和recvmsg。这两个函数的参数中都有一个指向msghdr结构的指针,该结构包含了所有有关收发内容的信息。该结构的定义如下:
struct msghdr {
void *msg_name;
int msg_namelen;
struct iovec *msg_iov;
__kernel_size_t msg_iovlen;
void *msg_control;
__kernel_size_t msg_controllen;
unsigned msg_flags;
};

其中,头两个元素通常用于在网络连接上发送数据报文,在这里,目的地址可以由每个数据报文指定。下面两个元素使我们可以指定由多个缓冲区构成的数组(散布读和聚集写)。msg_flags字段包含了说明所接受到消息的标志。
两个元素处理控制消息的传送和接收。msg_control字段指向cmsghdr(控制信息首部)结构,msg_controllen字段包含控制信息的字节数。
struct cmsghdr {
socklen_t cmsg_len; /* data byte count, including header */
int cmsg_level; /* originating protocol */
int cmsg_type; /* protocol-specific type */
/* followed by unsigned char cmsg_data[]; */
};

为了发送文件描述符,将cmsg_len设置为cmsgghdr结构的长度加一个整型(描述符)的长度,cmsg_level字段设置为SOL_SOCKET,cmsg_type字段设置为SCM_RIGHTS,用以指明我们在传送访问权。(SCM指的是套接字级控制消息,socket_level control message。)访问权仅能通过UNIX域套接字传送。描述符紧随cmsg_type字段之后存放,用CMSG_DATA宏获取该整型量的指针。