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

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

作者: QuietHeart | 来源:发表于2020-08-09 22:21 被阅读0次

    3、Unix域套接字

    UNIX域套接字用于和运行在同一台机器上面的进程进行通信。尽管因特网域的套接字也可以用于同样的目的,但是UNIX域的套接字的效率更高。UNIX域的套接字只拷贝数据,它门没有对协议的相关处理,没有网络头的添加和移除,没有校验和的计算,没有顺序号码的生成,也没有对发送的确认。

    UNIX域套接字同时提供了流和数据报的接口。但是UNIX域数据报服务是可靠的,消息不会丢失也不会乱序。UNIX域套接字类似套接字和pipes的综合。你可以使用面向网络的套接字接口来使用它们或者你也可以使用socketpair函数创建一对匿名的,连接的UNIX域套接字。

    匿名UNIX域套接字

    #include <sys/socket.h>
    int socketpair(int domain, int type, int protocol, int sockfd[2]);
    

    返回:如果成功返回0,如果错误返回1。

    尽管这个接口足够通用可以允许socketpair在任何域中使用,但是操作系统一般只支持对UNIX域的支持。

    举例:使用UNIX域套接字的s_pipe函数

    下面的代码展示了基于套接字的s_pipe函数实现。这个函数创建一对面向连接的UNIX域流的套接字。

    有些基于BSD的系统使用UNIX 域套接字来实现管道,但是当pipe被调用的时候,管道第一个描述符号的读端以及第二个描述符号的写端都是关闭的。为了获得一个全双工的管道,我们必须直接调用socketpair。

    #include "apue.h"
    #include <sys/socket.h>
    
    /*
     * Returns a full-duplex "stream" pipe (a UNIX domain socket)
     * with the two file descriptors returned in fd[0] and fd[1].
     */
    int s_pipe(int fd[2])
    {
        return(socketpair(AF_UNIX, SOCK_STREAM, 0, fd));
    }
    

    有名UNIX域套接字

    尽管socketpair函数创建了互相连接的套接字,但是它们并没有名字。也就是说,它们不能通过没有亲属关系的进程被访问到。在前面,我们学到了如何将一个地址绑定到一个因特网的套接字上面。如同因特网的套接字一样,UNIX域套接字可以有名称并且用来做为某些服务的通知。然而,UNIX域的套接字和因特网的套接字有所不同。

    在前面我们知道, 套接字的地址格式对于每个系统实现来说有所不同 。一个用于UNIX域套接字的地址可以使用sockaddr_un结构来进行表示。

    在Linux 2.4.22和Solaris 9中,sockaddr_un结构如下,并定义在<sys/un.h>中:

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

    在FreeBSD 5.2.1和Mac OS X 10.3中,sockaddr_un结构的定义如下:

    struct sockaddr_un {
            unsigned char  sun_len;         /* length including null */
            sa_family_t    sun_family;      /* AF_UNIX */
            char           sun_path[104];   /* pathname */
    };
    

    sockaddr_un结构中的sun_path成员包含了一个路径。当我们将一个地址绑定到一个UNIX域的套接字上面的时候,系统以同样的名字创建了一个S_IFSOCK类型的文件。这个文件只用来通知客户程序套接字的名称。这个文件不能被打开只能用于应用程序之间的通信。

    如果这个文件在我们想要绑定同样名称的地址的时候已经存在了,这个绑定的请求将会失败。当我们关闭套接字的时候,文件不会被自动地删除,所以我们需要确保当我们程序退出的时候将这个文件unlink。

    举例:绑定一个地址到UNIX域套接字

    后面的代码展示了绑定一个地址到UNIX域套接字的例子。

    #include "apue.h"
    #include <sys/socket.h>
    #include <sys/un.h>
    int main(void)
    {
        int fd, size;
        struct sockaddr_un un;
    
        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);
    }
    

    当我们运行这个程序的时候,绑定请求成功,但是如果我们再次运行这个程序,我们会得到一个错误。因为,这个文件已经存在了,如果我们不删除这个文件那么这个程序不会再次成功运行。

    $ ./a.out                                       run the program
    UNIX domain socket bound
    $ ls -l foo.socket                              look at the socket file
    srwxrwxr-x 1 sar        0 Aug 22 12:43 foo.socket
    $ ./a.out                                       try to run the program again
    bind failed: Address already in use
    $ rm foo.socket                                 remove the socket file
    $ ./a.out                                       run the program a third time
    UNIX domain socket bound                        now it succeeds
    

    这里,我们确定bind的地址的大小的方法是使用offsetof函数来确定sun_path成员的偏移再加上其大小,并 没有包含其中的NULL字符串结束字节 。因为不同的系统实现sun_path成员前面都有什么是不一样的,我们使用<stddef.h>中的offsetof宏来从结构的最开始进行计算。这个offsetof宏的定义如下:

    #define offsetof(TYPE, MEMBER) ((int)&((TYPE *)0)->MEMBER)
    

    这个表达式会计算一个整数,这个整数代表一个成员的起始地址,并且假定整个结构的起始地址是0。

    相关文章

      网友评论

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

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