进程间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接收数据进行处理。处理完成后通过管理套接字通知父进程。
 
 

你可能感兴趣的:(进程间SOCKET句柄传递-进程池应用)