美文网首页LinuxLinux学习之路
APUE读书笔记-17高级进程通信(6)

APUE读书笔记-17高级进程通信(6)

作者: QuietHeart | 来源:发表于2020-08-10 16:05 被阅读0次

    举例:单向连接(客户服务端模式的常见问题)

    服务进程可以使用标准的bind,listen和accept函数来管理unix域到客户进程的单一连接。客户进程使用connect来和服务进程进行连接;在服务进程接收到了connect请求之后,在客户和服务进程之间就存在了一条单一的连接。这样的操作和前面我们在因特网套接字中的两个例子类似。

    unix域套接字的serv_listen函数

    下面给出一个使用unix域套接字的serv_listen函数:

    #include "apue.h"
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <errno.h>
    #define QLEN 10
    /*
     * Create a server endpoint of a connection.
     * Returns fd if all OK, <0 on error.
     */
    int serv_listen(const char *name)
    {
        int                 fd, len, err, rval;
        struct sockaddr_un  un;
    
        /* create a UNIX domain stream socket */
        if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
           return(-1);
        unlink(name);   /* in case it already exists */
    
        /* fill in socket address structure */
        memset(&un, 0, sizeof(un));
        un.sun_family = AF_UNIX;
        strcpy(un.sun_path, name);
        len = offsetof(struct sockaddr_un, sun_path) + strlen(name);
    
        /* bind the name to the descriptor */
        if (bind(fd, (struct sockaddr *)&un, len) < 0) {
            rval = -2;
            goto errout;
        }
    
        if (listen(fd, QLEN) < 0) { /* tell kernel we're a server */
            rval = -3;
            goto errout;
        }
        return(fd);
    
    errout:
        err = errno;
        close(fd);
        errno = err;
        return(rval);
    }
    

    首先,我们调用socket创建一个unix域的套接字。

    然后我们用一个已知的名字填充sockaddr_un结构(这个结构做为bind的参数)以便绑定给套接字。注意在一些平台上面我们不需要设定sun_len成员,因为操作系统通过传给bind函数的地址长度会为我们设置这个成员。

    最后,我们调用listen函数来告诉内核,进程将作为一个服务进程,等待来自客户进程的连接。

    unix域套接字的serv_accept函数

    当客户的连接请求到达的时候,服务进程再调用serv_accept函数。如下:

    #include "apue.h"
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <time.h>
    #include <errno.h>
    
    #define STALE   30  /* client's name can't be older than this (sec) */
    
    /*
     * Wait for a client connection to arrive, and accept it.
     * We also obtain the client's user ID from the pathname
     * that it must bind before calling us.
     * Returns new fd if all OK, <0 on error
     */
    int serv_accept(int listenfd, uid_t *uidptr)
    {
        int                 clifd, len, err, rval;
        time_t              staletime;
        struct sockaddr_un  un;
        struct stat         statbuf;
    
        len = sizeof(un);
        if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0)
            return(-1);     /* often errno=EINTR, if signal caught */
    
        /* obtain the client's uid from its calling address */
        len -= offsetof(struct sockaddr_un, sun_path); /* len of pathname */
        un.sun_path[len] = 0;           /* null terminate */
    
        if (stat(un.sun_path, &statbuf) < 0) {
            rval = -2;
            goto errout;
        }
    #ifdef S_ISSOCK     /* not defined for SVR4 */
        if (S_ISSOCK(statbuf.st_mode) == 0) {
            rval = -3;      /* not a socket */
            goto errout;
        }
    #endif
        if ((statbuf.st_mode & (S_IRWXG | S_IRWXO)) ||
            (statbuf.st_mode & S_IRWXU) != S_IRWXU) {
              rval = -4;     /* is not rwx------ */
              goto errout;
        }
    
        staletime = time(NULL) - STALE;
        if (statbuf.st_atime < staletime ||
            statbuf.st_ctime < staletime ||
            statbuf.st_mtime < staletime) {
              rval = -5;    /* i-node is too old */
              goto errout;
        }
        if (uidptr != NULL)
            *uidptr = statbuf.st_uid;   /* return uid of caller */
        unlink(un.sun_path);        /* we're done with pathname now */
        return(clifd);
    errout:
        err = errno;
        close(clifd);
        errno = err;
        return(rval);
    }
    

    服务进程阻塞在accept调用上面,等待客户进程调用cli_conn。当accept返回的时候,它的返回值是连接到客户进程的一个新的文件描述符号(这个和connld模块对STREAMS子系统所做的一样)。另外,客户端赋值到它的套接字上面的路径名称(这个名称包含客户进程的进程ID)也会在accept中,通过第二个参数(指向sockaddr_un结构的指针)被返回(参见下面的cli_conn)。我们给这个路径名称赋值一个null结束符号,然后调用stat。这样我们检测路径名称确实是一个套接字并且权限只允许用户读,写,执行。我们也会检查和套接字相关的时间不会超过30秒。

    如果所有这三项检测成功,我们假定客户进程的标识(它的有效用户ID)就是套接字的属主。尽管检测不是很完美,但是这也是我们在目前的系统上面可以做的最好的了(若内核返回有效用户ID给accept,就像ioctl的I_RECVFD命令那样,这会更好)。

    相关文章

      网友评论

        本文标题:APUE读书笔记-17高级进程通信(6)

        本文链接:https://www.haomeiwen.com/subject/hjhwrktx.html