c语言socket句柄函数传递,进程间SOCKET句柄传递-进程池应用

1。函数及结构介绍

开发通信程序,经常遇到服务程序的开发,常见使用进程池及线程池,下面介绍一对在进程池应用非常有用的函数,sendmsg/recvmsg,可以通过这组函数在进程中传递socket描述符。可以在主进程使用多路复用侦听socket事件,将读写事件句柄传递给工作进程进行处理。

在使用函数前先来看一看下面的结构

struct msghdr {

void    *msg_name;       /* Socket name */

int     msg_namelen;    /* Length of name */

struct iovec *msg_iov;   /* Data blocks */

__kernel_size_t msg_iovlen;    /* Number of blocks  */

void    *msg_control;

/* Per protocol magic (eg BSD file descriptor passing) */

__kernel_size_t msg_controllen; /* Length of cmsg list */

unsigned msg_flags;

};

msg_name,msg_namelen指向地址指针,用于套接口未连接场合如UPD套接字,如果无需指明协议地址如TCP协议,msg_name通常为NULL;

msg_iov,msg_iovlen指定输入输出缓冲区数组及长度;

msg_control, msg_controllen指定可选辅助数据的位置及大小,也称为控制信息;

msg_flags 只有recvmsg使用msg_flags成员,recvmsg被调用时,flags参数被拷贝到msg_flags成员,sendmsg忽略此成员,因为它使用flags参数驱动发送处理过程;

在使用recvmsg/sendmsg传递socket描述符时,msg_control成员指向的缓冲区被填以一个cmsghdr的结构:

struct cmsghdr

{

socklen_t cmsg_len;

int cmsg_level;

int cmsg_type;

/*下面可加入传输入数据,此处可写入描述符*/

}

下表列出辅助数据的各种用途:

协议

cmsg_level

cmsg_type

说明

TCP

SOL_SOCKET

SCM_RIGHTS

SCM_CREDS

发送/接必描述符,转交控制权

发送/接收用户凭证

IPv4

IPPROTO_IP

IP_RECVDSTADDR

IP_RECVIF

随UDP数据报接收宿地址

随UPD数据报接收接口索引

IPv6

IPPROTO_IPV6

IPV6-DSTOPTS

IPV6-HOPLIMIT

IPV6-HOPOPTS

IPV6-NEXTHOP

IPV6-PKTINFO

IPV6-RTHDR

IPV6-TCLASS

指定/接收目的地址选项

指定/接收跳限

指定/接收步跳选项

指定下一跳地址

指定/接收分组信息

指定/接收接收路由头部

指定/接收分组流通类别

宏定义CMSG_DATA指向与cmsghdr结构关联的数据的指针

函数:

ssize_t sendmsg(int sockfd, struct msghdr *msg, int flags);

ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);

2。例子代码

例子:

写socket描述符:

#define CONTROLLEN sizeof (struct cmsghdr) + sizeof (int)

int send_fd (int sockfd, int fd)

{

char tmpbuf[CONTROLLEN];

struct cmsghdr *cmptr = (struct cmsghdr *) tmpbuf;

struct iovec iov[1];

struct msghdr msg;

char buf[1];

iov[0].iov_base = buf;

iov[0].iov_len = 1;

msg.msg_iov = iov;

msg.msg_iovlen = 1;

msg.msg_name = NULL;

msg.msg_namelen = 0;

msg.msg_control = cmptr;

msg.msg_controllen = CONTROLLEN;

cmptr->cmsg_level = SOL_SOCKET;

cmptr->cmsg_type = SCM_RIGHTS;

cmptr->cmsg_len = CONTROLLEN;

*(int *)CMSG_DATA (cmptr) = fd;

for( ; ; ) {

if (sendmsg(sockfd, &msg, 0) != 1) {

if( EINTR == errno ) {

continue;

} else {

return -1;    }

} else {

break;

}

}

return 0;

}

读socket描述符:

int recv_fd( int sockfd )

{

char tmpbuf[CONTROLLEN];

struct cmsghdr *cmptr = (struct cmsghdr *) tmpbuf;

struct iovec iov[1];

struct msghdr msg;

char buf[1];

iov[0].iov_base = buf;

iov[0].iov_len = sizeof (buf);

msg.msg_iov = iov;

msg.msg_iovlen = 1;

msg.msg_name = NULL;

msg.msg_namelen = 0;

msg.msg_control = cmptr;

msg.msg_controllen = CONTROLLEN;

for( ; ; ) {

if (recvmsg(sockfd, &msg, 0) <= 0) {

if( EINTR == errno ) {

continue;

}

else {

return -1;

}

} else {

break;

}

}

return *(int *) CMSG_DATA (cmptr);

}

另外,传出socket描述符的进程当传出成功时需关闭socket描述符。

3。应用

进程间socket描述符传递一般应用在进程池中,父子进程通过socketpair创建用于进程间通信的管理套接字,父进程侦听端口,当有连接时accept后将此sockfd传递给工作子进程。由子进程从此sockfd接收数据进行处理。处理完成后通过管理套接字通知父进程。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在 Unix/Linux 系统下,我们可以使用 `sendmsg` 和 `recvmsg` 函数传递文件句。下面是一个简单的示例代码: #### 发送进程代码 ```c #include <sys/types.h> #include <sys/socket.h> #include <sys/uio.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <errno.h> #define CONTROLLEN CMSG_LEN(sizeof(int)) int send_fd(int fd, int fd_to_send) { struct iovec iov[1]; struct msghdr msg; char buf[2]; // 发送任意数据 int ret; union { struct cmsghdr cm; char control[CONTROLLEN]; } control_un; struct cmsghdr *pcmsg; iov[0].iov_base = buf; iov[0].iov_len = 2; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; if (fd_to_send >= 0) { msg.msg_control = control_un.control; msg.msg_controllen = CONTROLLEN; pcmsg = CMSG_FIRSTHDR(&msg); pcmsg->cmsg_len = CONTROLLEN; pcmsg->cmsg_level = SOL_SOCKET; pcmsg->cmsg_type = SCM_RIGHTS; *(int *)CMSG_DATA(pcmsg) = fd_to_send; } else { msg.msg_control = NULL; msg.msg_controllen = 0; buf[1] = 0; } buf[0] = 0; if ((ret = sendmsg(fd, &msg, 0)) < 0) { perror("sendmsg"); } return ret; } ``` #### 接收进程代码 ```c int recv_fd(int fd) { struct iovec iov[1]; struct msghdr msg; char buf[2]; int ret; union { struct cmsghdr cm; char control[CONTROLLEN]; } control_un; struct cmsghdr *pcmsg; iov[0].iov_base = buf; iov[0].iov_len = 2; msg.msg_iov = iov; msg.msg_iovlen = 1; msg.msg_name = NULL; msg.msg_namelen = 0; msg.msg_control = control_un.control; msg.msg_controllen = CONTROLLEN; if ((ret = recvmsg(fd, &msg, 0)) < 0) { perror("recvmsg"); return -1; } if (buf[0] != 0) { printf("error: received %d bytes\n", ret); return -1; } if ((pcmsg = CMSG_FIRSTHDR(&msg)) != NULL && pcmsg->cmsg_len == CONTROLLEN) { if (pcmsg->cmsg_level != SOL_SOCKET) { printf("error: control level != SOL_SOCKET\n"); return -1; } if (pcmsg->cmsg_type != SCM_RIGHTS) { printf("error: control type != SCM_RIGHTS\n"); return -1; } return *(int *)CMSG_DATA(pcmsg); } else { return -1; } } ``` 使用示例: ```c #include <sys/types.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> int main() { int fd[2]; int ret; char buf[256]; if (socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0) { perror("socketpair"); exit(1); } int send_fd = open("file.txt", O_RDONLY); if (send_fd < 0) { perror("open"); exit(1); } if ((ret = fork()) < 0) { perror("fork"); exit(1); } if (ret == 0) { // 子进程 close(fd[0]); int recv_fd = recv_fd(fd[1]); if (recv_fd < 0) { exit(1); } printf("received fd: %d\n", recv_fd); read(recv_fd, buf, 256); printf("read: %s\n", buf); close(recv_fd); exit(0); } else { // 父进程 close(fd[1]); send_fd(fd[0], send_fd); close(send_fd); waitpid(ret, NULL, 0); exit(0); } } ``` 在这个示例中,父进程打开了 `file.txt` 文件,并将其文件描述符通过 `send_fd` 函数发送给了子进程。子进程通过 `recv_fd` 函数接收到了文件描述符,并读取了文件中的内容。注意,这个示例中并没有考虑错误处理,实际应用中需要根据实际情况进行完善。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值