进程间传递描述符二

进程间传递描述符二

发送、接收描述符

发送描述符

经过了前面的准备工作,是时候发送描述符了,先来看看函数原型:

int write_fd(int fd, void *ptr, int nbytes, int sendfd);

参数说明如下:

@fd :发送 TCP 套接字接口;这个可以是使用socketpair返回的发送套接字接口

@ptr :发送数据的缓冲区指针;

@nbytes :发送的字节数;

@sendfd :向接收进程发送的描述符;

函数返回值为写入的字节数, <0 说明发送失败;

废话少说,代码先上,发送描述符的代码相对简单一些,说明见代码内注释。

先说明一下,旧的 Unix 系统使用的是 msg_accrights 域来传递描述符,因此我们需要使用宏 HAVE_MSGHDR_MSG_CONTROL 以期能同时支持这两种版本。

int write_fd(int fd, void *ptr, int nbytes, int sendfd) { struct msghdr msg; struct iovec iov[1]; // 有些系统使用的是旧的msg_accrights域来传递描述符,Linux下是新的msg_control字段 #ifdef HAVE_MSGHDR_MSG_CONTROL union{ // 前面说过,保证cmsghdr和msg_control的对齐 struct cmsghdr cm; char control[CMSG_SPACE(sizeof(int))]; }control_un; struct cmsghdr *cmptr; // 设置辅助缓冲区和长度 msg.msg_control = control_un.control; msg.msg_controllen = sizeof(control_un.control); // 只需要一组附属数据就够了,直接通过CMSG_FIRSTHDR取得 cmptr = CMSG_FIRSTHDR(&msg); // 设置必要的字段,数据和长度 cmptr->cmsg_len = CMSG_LEN(sizeof(int)); // fd类型是int,设置长度 cmptr->cmsg_level = SOL_SOCKET; cmptr->cmsg_type = SCM_RIGHTS; // 指明发送的是描述符 *((int*)CMSG_DATA(cmptr)) = sendfd; // 把fd写入辅助数据中 #else msg.msg_accrights = (caddr_t)&sendfd; // 这个旧的更方便啊 msg.msg_accrightslen = sizeof(int); #endif // UDP才需要,无视 msg.msg_name = NULL; msg.msg_namelen = 0; // 别忘了设置数据缓冲区,实际上1个字节就够了 iov[0].iov_base = ptr; iov[0].iov_len = nbytes; msg.msg_iov = iov; msg.msg_iovlen = 1; return sendmsg(fd, &msg, 0); } 

  接收描述符

发送方准备好之后,接收方准备接收,函数原型为:

int read_fd(int fd, void *ptr, int nbytes, int *recvfd);

参数说明如下:

@fd :接收 TCP 套接字接口; 这个可以是使用 socketpair返回的接收套接字接口

@ptr :接收数据的缓冲区指针;

@nbytes :接收缓冲区大小;

@recvfd :用来接收发送进程发送来的描述符;

函数返回值为读取的字节数, <0 说明读取失败;

接收函数代码如下,相比发送要复杂一些。

int read_fd(int fd, void *ptr, int nbytes, int *recvfd) { struct msghdr msg; struct iovec iov[1]; int n; int newfd; #ifdef HAVE_MSGHDR_MSG_CONTROL union{ // 对齐 struct cmsghdr cm; char control[CMSG_SPACE(sizeof(int))]; }control_un; struct cmsghdr *cmptr; // 设置辅助数据缓冲区和长度 msg.msg_control = control_un.control; msg.msg_controllen = sizeof(control_un.control); #else msg.msg_accrights = (caddr_t) &newfd; // 这个简单 msg.msg_accrightslen = sizeof(int); #endif // TCP无视 msg.msg_name = NULL; msg.msg_namelen = 0; // 设置数据缓冲区 iov[0].iov_base = ptr; iov[0].iov_len = nbytes; msg.msg_iov = iov; msg.msg_iovlen = 1; // 设置结束,准备接收 if((n = recvmsg(fd, &msg, 0)) <= 0) { return n; } #ifdef HAVE_MSGHDR_MSG_CONTROL // 检查是否收到了辅助数据,以及长度,回忆上一节的CMSG宏 cmptr = CMSG_FIRSTHDR(&msg); if((cmptr != NULL) && (cmptr->cmsg_len == CMSG_LEN(sizeof(int)))) { // 还是必要的检查 if(cmptr->cmsg_level != SOL_SOCKET) { printf("control level != SOL_SOCKET/n"); exit(-1); } if(cmptr->cmsg_type != SCM_RIGHTS) { printf("control type != SCM_RIGHTS/n"); exit(-1); } // 好了,描述符在这 *recvfd = *((int*)CMSG_DATA(cmptr)); } else { if(cmptr == NULL) printf("null cmptr, fd not passed./n"); else printf("message len[%d] if incorrect./n", cmptr->cmsg_len); *recvfd = -1; // descriptor was not passed } #else if(msg.msg_accrightslen == sizeof(int)) *recvfd = newfd; else *recvfd = -1; #endif return n; }

发送和接收函数就这么多,就像上面看到的,进程间传递套接字还是有点麻烦的。Linux的就介绍完了,后面在简单说说Windows是如何传递的,话说MSDN真的方便哪。

 

你可能感兴趣的:(linux,struct,socket,tcp,null,Descriptor)