• 《网络编程》Unix 域套接字


    概述

            Unix 域套接字是一种client和server在单主机上的 IPC 方法。Unix 域套接字不运行协议处理,不须要加入或删除网络报头,无需验证和,不产生顺序号,无需发送确认报文,比因特网域套接字的效率更高。Unix 域套接字提供字节流(类似于 TCP)和数据报(类似于 UDP)两种接口,UNIX域数据报服务是可靠的,既不会丢失消息也不会传递出错。UNIX域套接字是套接字和管道之间的混合物。


    Unix 域套接字编程


    地址结构:

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

    存放在 sun_path 数组中的路径名必须以空字符结尾。以下是把一个路径名绑定到 Unix 域套接字上实现的程序:

    /* 创建一个Unix域套接字,并bind一个路径名 */
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stddef.h>
    
    extern void err_sys(const char *, ...);
    extern void err_quit(const char *, ...);
    int main(int argc, char **argv)
    {
        int sockfd, size;
        socklen_t len;
        struct sockaddr_un addr1, addr2;
    
        if(argc != 2)
            err_quit("usage: %s <pathname>", argv[0]);
    
        bzero(&addr1, sizeof(addr1));
        addr1.sun_family = AF_UNIX;
    
        strncpy(addr1.sun_path, argv[1], sizeof(addr1.sun_path)-1);
    
        /* 创建一个Unix域套接字 */
        if( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
            err_sys("socket error");
    
        /* 若路径名在文件系统已存在,则bind会出错;所以先调用unlink删除要绑定的路径名,防止bind出错 */
        unlink(argv[1]);
    
        /* 将路径名bind绑定到该套接字上 */
        size = offsetof(struct sockaddr_un, sun_path) + strlen(addr1.sun_path);
        if(bind(sockfd, (struct sockaddr *)&addr1, size) < 0)
            err_sys("bind error");
    
        /* 显示已绑定的路径名 */
        len = sizeof(addr2);
        getsockname(sockfd, (struct sockaddr *)&addr2, &len);
        printf("bound name = %s, returned len = %d
    ", addr2.sun_path, len);
    
        exit(0);
    }
    

    $ ./main /tmp/sock
    bound name = /tmp/sock, returned len = 12
    
    /*当该路径名存在,且不使用unlink函数时,会出现下面提示*/
    $ ./main /tmp/sock
    bind error: Address already in use


    为了创建一对非命名的,相互连接的 UNXI 域套接字,用户能够使用socketopair函数。事实上现例如以下:

    #include <sys/socket.h>
    int socketpair(int domain, int type, int protocol, int sockfd[2]);
    /* 返回值:若成功则返回0,出错则返回-1 */
    /* 说明
    * 參数 domain 必须是 AF_LOCAL 或 AF_UNIX,protocol 必须为 0,type 能够是 SOCK_STREAM 或 SOCK_DGRAM,新创建的两个套接字描写叙述符作为sockfd[0]和sockfd[1]返回;

    Unix 域套接字函数

    与因特网域套接字相比,Unix 域套接字有下面的差别:

    1. 由 bind 创建的路径名默认訪问权限应为 0777,并按当前 umask 值进行改动;
    2. 路径名必须是一个绝对路径名,避免使用相对路径名。由于它的解析依赖于调用者的当前工作文件夹,若server绑定的是一个相对路径名,则client和server必须在同样的文件夹才干正常工作;
    3. 在 connect 调用中指定的路径名必须是一个当前绑定在某个已打开的 Unix 域套接字上的路径名,并且套接字类型必须一致;
    4. 调用 connect 连接一个 Unix 域套接字涉及的权限測试等价于调用 open 以仅仅写方式訪问对应的路径名;
    5. Unix 域字节流套接字类似于 TCP 套接字:它们都为进程提供一个无记录边界的字节流接口;
    6. 在 Unix 域字节流套接字中,若 connect 调用时发现监听套接字的队列已满,则马上返回 ECONNREFUSED 错误。而 TCP 套接字遇到这样的情况,TCP 监听套接字忽略这些到达的 SYN 连接请求,TCP client则会重发数次 SYN 报文段;
    7. Unix 域数据报套接字类似于 UDP 套接字:它们都提供一个保留记录边界的不可靠数据报;
    8. 为一个未绑定路径名的 Unix 套接字发送数据时,不会自己主动给该套接字绑定一个路径名。而 UDP 套接字在给一个未绑定的 UDP 套接字发送数据时,会自己主动为其绑定一个暂时port;

    Unix 域字节流编程

    server程序:

    #include <sys/socket.h>
    #include <sys/wait.h>
    #include <sys/un.h>
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stddef.h>
    #include <signal.h>
    #include <errno.h>
    
    #define QLEN 1024
    typedef void Sigfunc(int);
    
    extern void err_sys(const char *, ...);
    extern void err_quit(const char *, ...);
    extern void str_echo(int);
    static Sigfunc *MySignal(int signo, Sigfunc *func);
    static Sigfunc *M_signal(int signo, Sigfunc *func);
    static void sig_chld(int);
    
    int main(int argc, char **argv)
    {
        int sockfd, conndfd, size;
        socklen_t len;
        pid_t childpid;
        struct sockaddr_un cliaddr, servaddr;
    
        if(argc != 2)
            err_quit("usage: %s <pathname>", argv[0]);
    
        bzero(&servaddr, sizeof(servaddr));
        servaddr.sun_family = AF_UNIX;
    
        strcpy(servaddr.sun_path, argv[1]);
    
        /* 创建一个Unix域套接字 */
        if( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
            err_sys("socket error");
    
        /* 若路径名在文件系统已存在,则bind会出错;所以先调用unlink删除要绑定的路径名,防止bind出错 */
        unlink(argv[1]);
    
        /* 将路径名bind绑定到该套接字上 */
        size = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path);
        if(bind(sockfd, (struct sockaddr *)&servaddr, size) < 0)
            err_sys("bind error");
    
        /* 监听套接字 */
        if(listen(sockfd, QLEN) < 0)
        {
            close(sockfd);
            err_sys("listen error");
        }
    
        /* 信号处理 */
        MySignal(SIGCHLD, sig_chld);
    
        for( ; ;)
        {
            len = sizeof(cliaddr);
            if( (conndfd = accept(sockfd, (struct sockaddr *)&cliaddr, &len)) < 0)
            {
                if(errno == EINTR)
                    continue;
                else
                    err_sys("accept error");
            }
        }
    
        if( (childpid = fork()) == 0)
        {
            close(sockfd);
            str_echo(conndfd);
            exit(0);
        }
        close(conndfd);
    }
    
    void sig_chld(int signo)
    {
        pid_t pid;
        int stat;
        while( (pid = waitpid(-1, &stat, WNOHANG)) > 0)
            printf("child %d terminated
    ", pid);
        return;
    }
    static Sigfunc *MySignal(int signo, Sigfunc *func)
    {
        Sigfunc *sigfunc;
        if( (sigfunc = M_signal(signo, func)) == SIG_ERR)
            err_sys("signal error");
        return (sigfunc);
    }
    
    static Sigfunc *M_signal(int signo, Sigfunc *func)
    {
        struct sigaction act, oact;
    
        /* 设置信号处理函数 */
        act.sa_handler = func;
        /* 初始化信号集 */
        sigemptyset(&act.sa_mask);
        act.sa_flags = 0;
        if(signo == SIGALRM)
        {/* 若是SIGALRM信号,则系统不会自己主动重新启动 */
    #ifdef SA_INTERRUPT
            act.sa_flags |= SA_INTERRUPT;
    #endif
        }
        else
        {/* 其余信号设置为系统会自己主动重新启动 */
    #ifdef SA_RESTART
            act.sa_flags |= SA_RESTART;
    #endif
        }
        /* 调用 sigaction 函数 */
        if(sigaction(signo, &act, &oact) < 0)
            return(SIG_ERR);
        return(oact.sa_handler);
    }
    

    client程序:

    #include <sys/socket.h>
    #include <sys/un.h>
    #include <string.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <errno.h>
    
    
    extern void err_sys(const char *, ...);
    extern void err_quit(const char *, ...);
    extern void str_cli(FILE *, int);
    
    int
    main(int argc, char **argv)
    {
    	int					sockfd;
    	struct sockaddr_un	servaddr;
    
        if(argc != 2)
            err_quit("usage: %s <pathname>", argv[0]);
    	if( (sockfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
            err_sys("socket error");
    
    	bzero(&servaddr, sizeof(servaddr));
    	servaddr.sun_family = AF_UNIX;
    	strcpy(servaddr.sun_path, argv[1]);
    
        int err;
    	err = connect(sockfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
    	if( err < 0)
            err_sys("connect error");
    
    	str_cli(stdin, sockfd);		/* do it all */
    
    	exit(0);
    }
    



    參考资料:

    《Unix 网络编程》

  • 相关阅读:
    关于迭代器模式的个人理解
    设计模式之迭代器模式详解(foreach的精髓)
    关于备忘录模式的个人理解
    设计模式之备忘录模式详解(都市异能版)
    关于桥接模式的个人理解
    桥接模式详解(都市异能版)
    设计模式之 中介者模式
    关于命令模式个人的理解 撤销 重做的实现
    剑指offer 连续子数组的最大和
    剑指offer 删除链表中重复的结点
  • 原文地址:https://www.cnblogs.com/lcchuguo/p/4466413.html
Copyright © 2020-2023  润新知