将一个进程中的描述字传递到另一个进程,并且使得该描述字依然有效。
传递描述符并不是传递一个int型的描述符编号,而是在接收进程中创建一个新的描述符,并且在内核的文件表中,它与发送进程发送的描述符指向相同的项。
实现过程包含如下:
(1) 创建一个数据报的unix domain socket套接口。
(2) 发送进程打开一个文件并获取其描述符。
(3) 发送进程创建一个msghdr结构,将(2)中待传递的描述字作为辅助数据发送,调用sendmsg跨越(1)中获得的套接口发送(2)中的描述符。
(4) 接收进程调用recvmsg在(1)中获取的套接口上接收该描述符。
注意:在发送过程中由于没有报文,在接收的过程中会分不清是文件已经结束还是只是发送了辅助数据,因此通常在发送辅助数据的时候会传输至少一个字节的数据,该数据在接收过程中不做任何的处理。
Server服务端例子:
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 100
const char file_server_socket[] = "msghdr_tmp";
int main(int argc, char *argv[])
{
int listen_fd, cli_fd, rev_fd;
struct sockaddr_un server_addr, client_addr;
struct msghdr socket_msg;
struct cmsghdr *ctrl_msg;
struct iovec iov[1];
socklen_t cli_len;
int ret;
char buf[BUFFER_SIZE];
char *test_msg = "msg for test.\n";
char ctrl_data[CMSG_SPACE(sizeof(listen_fd))];
/* remove socket file fisrtly */
remove(file_server_socket);
/* create socket */
listen_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(listen_fd < 0) {
printf("server: create socket failed!\n");
return -1;
}
/* initilize sockaddr_un */
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, file_server_socket);
/* bind the socket and addr */
ret = bind(listen_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(ret < 0) {
printf("server: bind socket failed! errno=%d.\n", errno);
close(listen_fd);
return -1;
}
/* listen the socket connect */
ret = listen(listen_fd, 5);
if(ret < 0) {
printf("server: listen socket failed! errno=%d.\n", errno);
close(listen_fd);
return -1;
}
printf("server: waiting for uds client to connect...\n");
while(1) {
/* accept to connect the client */
cli_len = sizeof(client_addr);
cli_fd= accept(listen_fd, (struct sockaddr *)&client_addr, &cli_len);
if(cli_fd < 0) {
printf("server: accept client failed!\n");
continue;
}
socket_msg.msg_name = NULL;
socket_msg.msg_namelen = 0;
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
socket_msg.msg_iov = iov;
socket_msg.msg_iovlen = 1;
socket_msg.msg_control = ctrl_data;
socket_msg.msg_controllen = sizeof(ctrl_data);
ret = recvmsg(cli_fd, &socket_msg, 0);
if(ret < 0) {
printf("server: recvmsg failed!\n");
return ret;
}
ctrl_msg = CMSG_FIRSTHDR(&socket_msg);
if(ctrl_msg != NULL && ctrl_msg->cmsg_len == CMSG_LEN(sizeof(listen_fd))) {
if(ctrl_msg->cmsg_level != SOL_SOCKET) {
printf("cmsg_level is not SOL_SOCKET\n");
continue;
}
if(ctrl_msg->cmsg_type != SCM_RIGHTS) {
printf("cmsg_type is not SCM_RIGHTS");
continue;
}
rev_fd = *((int *)CMSG_DATA(ctrl_msg));
printf("result: recv fd = %d\n", rev_fd);
write(rev_fd, test_msg, strlen(test_msg) + 1);
}
}
return 0;
}
Client客户端例子:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 100
#define OPEN_FILE "file_fd"
const char file_server_socket[] = "msghdr_tmp";
int main(int argc, char *argv[])
{
int fd, cli_fd;
struct sockaddr_un server_addr;
struct msghdr socket_msg;
struct cmsghdr *ctrl_msg;
struct iovec iov[1];
int ret;
char buf[BUFFER_SIZE];
char ctrl_data[CMSG_SPACE(sizeof(cli_fd))];
/* create socket */
cli_fd = socket(AF_UNIX, SOCK_STREAM, 0);
if(cli_fd < 0) {
printf("client: create socket failed!\n");
return -1;
}
fd = open(OPEN_FILE, O_CREAT | O_RDWR, 0777);
if(fd < 0) {
printf("open file_fd failed!\n");
return -1;
}
/* initilize sockaddr_un */
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sun_family = AF_UNIX;
strcpy(server_addr.sun_path, file_server_socket);
/* connect uds server */
ret = connect(cli_fd, (struct sockaddr *)&server_addr, sizeof(server_addr));
if(ret < 0) {
printf("client: connect to server failed!\n");
return -1;
}
socket_msg.msg_name = NULL;
socket_msg.msg_namelen = 0;
iov[0].iov_base = buf;
iov[0].iov_len = sizeof(buf);
socket_msg.msg_iov = iov;
socket_msg.msg_iovlen = 1;
socket_msg.msg_control = ctrl_data;
socket_msg.msg_controllen = sizeof(ctrl_data);
ctrl_msg = CMSG_FIRSTHDR(&socket_msg);
ctrl_msg->cmsg_len = CMSG_LEN(sizeof(cli_fd));
ctrl_msg->cmsg_level = SOL_SOCKET;
ctrl_msg->cmsg_type = SCM_RIGHTS;
*((int *)CMSG_DATA(ctrl_msg)) = fd;
ret = sendmsg(cli_fd, &socket_msg, 0);
if(ret < 0) {
printf("client: sendmsg failed!\n");
return ret;
}
printf("client: ret = %d.\n", ret);
return 0;
}
参考链接:
http://blog.chinaunix.net/uid-20937170-id-4247670.html
https://wenku.baidu.com/view/7a8a522158fb770bf78a55cf.html