linux进程间描述符的传递(sendmsg和recvmsg)

        将一个进程中的描述字传递到另一个进程,并且使得该描述字依然有效。

        传递描述符并不是传递一个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

你可能感兴趣的:(Linux,socket)