《unix环境高级编程》--- 高级进程间通信[1]

将一个地址绑定一UNIX域套接字
unix域套接字用于在同一台机器上运行的进程之间的通信。仅仅复制数据,不执行协议处理,不需要添加或删除网络报头,无需计算检验和,不产生顺序号,无需发送确认报文。比因特网域套接字效率更高。
unix域套接字时套接字和管道之间的混合物。
可以命名UNIX域套接字,将其用于无关进程之间。

#include "apue.h"
#include <sys/socket.h>
#include <sys/un.h>

int main(void)
{
    int fd, size;
    struct sockaddr_un un;

    /*
        struct sockaddr_un
        {
        sa_family_t sun_family;    AF_UNIX
        char sun_path[108];        pathname
        };

        将一地址绑定至UNIX域套接字时,系统用sun_path创建一类型为S_IFSOCK的文件。
        该文件不能打开,也不能用于通信,仅用于向客户进程告知套接字名字。关闭套接字时,
        不自动删除该文件,所以必须在应用程序终止前,对该文件执行解除链接操作。
    */  
    un.sun_family = AF_UNIX;
    strcpy(un.sun_path, "foo.socket");

    if((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
        err_sys("socket failed");

    size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);

    if(bind(fd, (struct sockaddr *)&un, size) < 0)
        err_sys("bind failed");
    printf("UNIX domain socket bound\n");

    exit(0);
}

这里写图片描述

经由UNIX域套接字传送文件描述符、凭证
在UNIX域套接字上发送凭证
在sendmsg调用中,发送双字节协议数据(null和status字节)和描述符。

#include "apue.h"
#include <sys/socket.h>

#if defined(SCM_CREDS)   /* BSD interface */
#define CREDSTRUCT cmsgcred
#define SCM_CREDTYPE SCM_CREDS
#elif defined(SCM_CREDENTIALS) /* Linux interface */
/*
   struct ucred
   {
    uint32_t  pid;   sender's process ID
    uint32_t  uid;   sender's user ID
    uint32_t  gid;   sender's group ID
   };
*/
#define CREDSTRUCT ucred
#define SCM_CREDTYPE SCM_CREDENTIALS
#else
#error passing credentials is unsupported!
#endif

/* size of control buffer to send/recv one file descriptor */
#define RIGHTSLEN CMSG_LEN(sizeof(int))
#define CREDSLEN CMSG_LEN(sizeof(struct CREDSTRUCT))
#define CONTROLLEN (RIGHTSLEN + CREDSLEN)

static struct cmsghdr *cmptr = NULL;  /* malloc'ed first time */

/* Pass a file descriptor to another process.
   If fd < 0, then -fd is sent back instead as the error status. */

int send_fd(int fd, int fd_to_send)
{
    struct CREDSTRUCT *credp;   
    /*
        struct cmsghdr
        {
        socklen_t  cmsg_len;      data byte conunt, including header
        int        cmsg_level;    originating protocol, e.g. SOL_SOCKET
        int        cmsg_type;     protocol-specific type, e.g. SCM_RIGHTS
            // followed by the actual control message data 
        };
    */
    struct cmsghdr *cmp;
    struct iovec iov[1];
    /*
        struct msghdr
        {
        void         *msg_name;       optional address
        socklen_t    msg_namelen;     address size in bytes
        struct iovec *msg_iov;        array of I/O buffers
        int          msg_iovlen;      number of elements in array
        void         *msg_control;    ancillary data, 指向cmsghdr(控制信息首部)
        socklen_t    msg_controllen;  number of ancillary bytes
        int          msg_flags;       flags for received message
        };
    */
    struct msghdr msg;
    char buf[2];   /* send_fd/recv_ufd 2-byte protocol */

    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_flags = 0;
    if(fd_to_send < 0)
    {
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        buf[1] = -fd_to_send;   /* nonzero status means error */
        if(buf[1] == 0)
            buf[1] = 1;  /* -256, etc. would screw up protocol */
    }
    else
    {
        if(cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
            return (-1);
        msg.msg_control = cmptr;
        msg.msg_controllen = CONTROLLEN;  /* 文件描述符+凭证 */
        cmp = cmptr;
        cmp->cmsg_level = SOL_SOCKET;
        cmp->cmsg_type = SCM_RIGHTS;  /* 指明在传输访问权 */
        cmp->cmsg_len = RIGHTSLEN;
        /*
           unsigned char *CMSG_DATA(struct cmsghdr *cp);
           返回值:指向与cmsghdr结构相关联的数据的指针
        */
        *(int *)CMSG_DATA(cmp) = fd_to_send;  /* the fd to pass */

        /*
           struct cmsghdr *CMSG_NXTHDR(struct msghdr *mp, 
                           struct cmsghdr *cp);
           返回值:指向与msghdr结构相关联的下一个cmsghdr结构的指针,该msghdr结构给出了
           当前cmsghdr结构,若当前cmsghdr结构已是最后一个则返回NULL。
        */
        cmp = CMSG_NXTHDR(&msg, cmp);
        cmp->cmsg_level = SOL_SOCKET;
        cmp->cmsg_type = SCM_CREDTYPE;
        cmp->cmsg_len = CREDSLEN;
        credp = (struct CREDSTRUCT *)CMSG_DATA(cmp);
     #if defined(SCM_CREDENTIALS)
        credp->uid = getuid();
        credp->gid = getgid();
        credp->pid = getpid();
     #endif
        buf[1] = 0;  /* zero status means OK */
    }
    buf[0] = 0;   /* null byte flag to recv_ufd() */
    if(sendmsg(fd, &msg, 0) != 2)
        return (-1);
    return (0);
}

在UNIX域套接字上接收凭证
为了接收文件描述符,为cmsghdr结构和描述符分配足够大的空间,将msg_control指向该存储空间,然后调用recvmsg。使用CMSG_LEN计算所需空间的总量。
从UNIX域套接字读入,直至读到null字节,位于最后的status字节前。null字节之前是
一条来自发送者的出错消息。

#include "apue.h"
#include <sys/socket.h>   /* struct msghdr */
#include <sys/un.h>

#if defined(SCM_CREDS)  /* BSD interface */
#define CREDSTRUCT cmsgcred
#define CR_UID cmcred_uid
#define CREDOPT LOCAL_PEERCRED
#define SCM_CREDTYPE SCM_CREDS
#elif defined(SCM_CREDENTIALS)  /* Linux interface */
#define CREDSTRUCT ucred
#define CR_UID uid
#define CREDOPT SO_PASSCRED
#define SCM_CREDTYPE SCM_CREDENTIALS
#else
#error passing credentials is unsupported!
#endif

/* size of control buffer to send/recv one file descriptor */
#define RIGHTSLEN CMSG_LEN(sizeof(int))
#define CREDSLEN  CMSG_LEN(sizeof(struct CREDSTRUCT))
#define CONTROLLEN (RIGHTSLEN + CREDSLEN)

static struct cmsghdr *cmptr = NULL;  /* malloc'ed first time */

/* Receive a file descriptor from a server process. Also, any data
   received is passed to (*userfunc)(STDERR_FILENO, buf, nbytes).
   We have a 2-byte protocol for receiving the fd from send_fd(). */
int recv_ufd(int fd, uid_t *uidptr, ssize_t (*userfunc)(int, const void *, size_t))
{
    struct cmsghdr *cmp;
    struct CREDSTRUCT *credp;
    int newfd, nr, status;
    char *ptr;
    char buf[MAXLINE];
    struct iovec iov[1];
    struct msghdr msg;
    const int on = 1;

    status = -1;
    newfd = -1;
    if(setsockopt(fd, SOL_SOCKET, CREDOPT, &on, sizeof(int)) < 0)
    {
        err_ret("setsocketopt failed");
        return (-1);
    }
    for(;;)
    {
        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;
        if(cmptr == NULL && (cmptr = malloc(CONTROLLEN)) == NULL)
            return (-1);
        msg.msg_control = cmptr;
        msg.msg_controllen = CONTROLLEN;
        if((nr = recvmsg(fd, &msg, 0)) < 0)
        {
            err_sys("recvmsg error");
        }
        else if(nr == 0)
        {
            err_ret("connnection closed by server");
            return (-1);
        }
        /* See if this is the final data with null & status. Null is next
           to last byte of buffer; status byte is last byte. Zero status
           means there is a file descriptor to receive */
        for(ptr = buf; ptr < &buf[nr];)
        {
            if(*ptr++ == 0)
            {
                if(ptr != &buf[nr-1])
                    err_dump("message format error");
                status = *ptr & 0xFF;  /* prevent sign extension */
                if(status == 0)
                {
                    if(msg.msg_controllen != CONTROLLEN)
                        err_dump("status = 0 but no fd");

                    /* process the control data */
                    for(cmp = CMSG_FIRSTHDR(&msg); cmp != NULL;
                        cmp = CMSG_NXTHDR(&msg, cmp))
                    {
                        if(cmp->cmsg_level != SOL_SOCKET)
                            continue;
                        switch(cmp->cmsg_type)
                        {
                        case SCM_RIGHTS:
                            newfd = *(int *)CMSG_DATA(cmp);
                            break;
                        case SCM_CREDTYPE:
                            credp = (struct CREDSTRUCT *)CMSG_DATA(cmp);
                            *uidptr = credp->CR_UID;
                            break;
                        default:
                            break;
                        }
                    }
                }
                else
                {
                    newfd = -status;
                }
                nr -= 2;
            }
        }
        if(nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr)
            return (-1);
        if(status >= 0)  /* final data has arrived */
            return (newfd);   /* descriptor, or -status */
    }
}

猜你喜欢

转载自blog.csdn.net/u012319493/article/details/80614878