20.辅助数据 和 传输描述字

打开一个文件或者一个套接口,如果想把这个已经打开的文件和套接口的描述字传给其他的进程(仅限于本机),可以通过发送unix域的辅助数据来完成。当然如果这2个进程是父子进程,则问题要简单很多,因为子进程继承了父进程打开的描述字。下面的问题是:如何在2个没有关系的进程之间传递描述字。

 

辅助数据又叫做控制信息,在之前介绍sendmsg和recvmsg中,对于数据结构struct msghd中有个字段void * msg_control;没有介绍。这就是辅助数据存储地方以及socklen_t msg_controllen;辅助数据的长度。基于字面意思也就是它可以用来传递一些比较特殊的信息。

 

辅助数据的结构:

struct  cmsghdr {

   socklen_t cmsg_len;

   int cmsg_level;

   int cmsg_type;

   /* followed by unsigned char cmsg_data[] */

}

 

如果是IPV4协议,则cmsg_level为IPPROTO_IP. cmsg_type值可以为:IP_RECVDSTADDR(接受UDP数据报的目的地址) 或者是IP_RECVIF (接受UDP数据报的接口索引)。

 

如果为IPV6协议,cmsg_level为IPPROTO_IPV6. cmsg_type值可以为:

IPV6_DSTOPTS(指定/接收目标选项)

IPV6_HOPLIMIT(指定/接收跳限)

IPV6_HOPOPTS(指定/接收步跳选项)

IPV6_NEXTHOP(指定下一跳地址)

IPV6_PKTINFO(指定/接收分组信息)

IPV6_RTHDR(指定/接收路由头部)

 

如果UNIX域协议,cmsg_level为SOL_SOCKET。cmsg_type值可以为:

SCM_RIGHTS(发送/接收描述字)

SCM_CREDS(发送/接收用户凭证)。

 

辅助数据有一个或者多个辅助数据对象组成,每个对象由cmsghdr开头,其后跟了数据,在cmsg_type和实际数据之间可能有填充字节,一个数据对象和下一个对象之间也可能有填充字节。之所以要有填充字节是为了使整个数据对象按照cmsg_type结构对齐。因此为了方便操作就定义了以下几种宏操作:

 

CMSG_FIRSTHDR(struct msghdr * msg);//返回第一个cmsghdr结构指针

CMSG_NXTHDR(struct msghdr * msg, struct cmsghdr * cmsgptr);//指向cmsgptr的下一个cmsghdr指针

CMSG_DATA(struct cmsghdr * cmsgptr);//指向cmsgptr相关联的数据的第一个字节指针

CMSG_LEN(unsigned int length); //给定数据量下,存储在cmsg_len中的位置

CMSG_SPACE(unsigned int length); //给定数据量下,一个辅助数据对象的大小

 

相对比较抽象,通过代码及注释来看下:

代码实现的是同机器上2个进程间通过unix域传递打开的文件描述字。

#include "/programe/net/head.h"
#include "sys/un.h"
#include "stdio.h"
#include "stdlib.h"
#include "unistd.h"
#include "string.h"

//打开监听套接字,等待从另一个进程传递过来打开的文件描述字信息
int main(int argc, char ** argv) {
        char buf[100];
        int sockfd;
        struct sockaddr_un addr;

        sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
        bzero(&addr, sizeof(addr));
        addr.sun_family = AF_LOCAL;
        strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path) - 1);
        unlink(argv[1]);
        int flag = bind(sockfd, (struct sockaddr *)&addr, SUN_LEN(&addr));//unix域,之前见过
        if(flag == -1) {
                printf("bind error\n");
                exit(1);
        }

        listen(sockfd, 10);

        int connfd = accept(sockfd, NULL, NULL);

        struct msghdr msg;
        struct iovec io;

        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        io.iov_base = buf;
        io.iov_len = 100;
        msg.msg_iov = &io;
        msg.msg_iovlen = 1; //msg普通数据的定义,在sendmsg与recvmsg里面见过.

        union {
                struct cmsghdr cmsg;
                char control[CMSG_SPACE(sizeof(int))];
        } control_un; //为了使整个数据结构对齐,所以定义了union对象,当然也可以使用malloc
        struct cmsghdr * cmptr;
        msg.msg_control = control_un.control;
        msg.msg_controllen = sizeof(control_un.control); //因为只有一个数据对象传送

        ssize_t size = recvmsg(connfd, &msg, 0);
        buf[size] = '\0';
        printf("get message:%s\n", buf);

        cmptr = CMSG_FIRSTHDR(&msg);//在接受到返回结果后,得到第一个控制信息对象
        if(!cmptr) {
                printf("some thing error!!!\n");
        } else {
                int recvfd = *((int *)CMSG_DATA(cmptr));//获取文件描述字信息
                char my[100];
                int length = read(recvfd, my, 100);
                my[length] = '\0';
                printf("%s\n", my);
                close(recvfd);
        }
        close(connfd);
        close(sockfd);
        exit(0);
}

 

 

#include "stdio.h"
#include "stdlib.h"
#include "/programe/net/head.h"
#include "string.h"
#include "sys/un.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "fcntl.h"
//打开一个文件,并将描述字传递给另一个进程
int main(int argc, char ** argv) {
        int sockfd;
        char buf[] = "hello world";
        int file = open("/programe/net/temp.txt", O_RDONLY);//打开文件,file就是即将要传输的描述字

        sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
        struct sockaddr_un addr;
        bzero(&addr, sizeof(addr));
        addr.sun_family = AF_LOCAL;
        strncpy(addr.sun_path, argv[1], sizeof(addr.sun_path) - 1);

        connect(sockfd, (struct sockaddr *)&addr, sizeof(struct sockaddr_un));

        struct msghdr msg;
        struct iovec io;

        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);

        cmptr = CMSG_FIRSTHDR(&msg);
        cmptr->cmsg_len = CMSG_LEN(sizeof(int));
        cmptr->cmsg_level = SOL_SOCKET;
        cmptr->cmsg_type = SCM_RIGHTS;//说明要传递的是描述字

        *((int *)CMSG_DATA(cmptr)) = file;//将描述字放入传输对象中

        msg.msg_name = NULL;
        msg.msg_namelen = 0;
        io.iov_base = buf;
        io.iov_len = sizeof(buf);
        msg.msg_iov = &io;
        msg.msg_iovlen = 1;

        ssize_t flag = sendmsg(sockfd, &msg, 0);
        printf("flag = %d\n", flag);
        close(sockfd);
        exit(0);

}

 

你可能感兴趣的:(数据结构,.net,socket,unix,sun)