• UNP学习 Unix域协议


    Unix域协议并不是一个实际的协议族,它只是在同一台主机上进行客户-服务器通信时,使用与在不同主机上的客户和服务器间通信时相同的API的一种方法。

    当客户和服务器在同一台主机上时,Unix域协议是这套系列书的第二卷将介绍的IPC通信方式的一种替代品。

    Unix域提供了两种类型的套接口:字节流套接口(与TCP类似)、数据报套接口(与UDP类似)

    使用Unix域套接口有三个原因:

    1. 当通信双方位于同一台主机上时,Unix域套接口的速度通常是TCP套接口的两倍。
    2. Unix域套接口可以用来在同一台主机上的各进程之间传递描述字。
    3. Unix域套接口的较新实现中可以向服务器提供客户的凭证,这能提供附加的安全检查。

    二、Unix域套接口地址结构

    struct sockaddr_un{
        uint8_t sun_len;
        sa_family_t sun_family;        /* AF_LOCAL */
        char sun_path[104];            /* null-terminated pathname */
    };

    三、socketpair函数

    socketpair函数建立一对相互连接的套接口,这个函数只对Unix域套接口适用

    #include <sys/socket.h>
    
    int socketpair(int family, int type, int protocol, int sockfd[2]);
    返回:成功0,出错-1

    family:必须为AF_LOCAL

    protocol:必须为0

    type:可以是SOCK_STREAM或SOCK_DGRAM

    新创建的两个套接口描述字作为sockfd[0]和sockfd[1]返回

     socketpair的多进程例子

     1 #include <stdio.h>
     2 #include <sys/types.h>
     3 #include <sys/socket.h>
     4 #include <pthread.h>
     5 
     6 #define SOCKET_BUFFER_SIZE (32768U)
     7 
     8 void *thread_function(void *arg)
     9 {
    10         int len = 0;
    11         int fd = *((int *)(arg));
    12         char buf[500];
    13         int cnt = 0;
    14 
    15         while(1) {
    16                 len = sprintf(buf, "Hi, main process, cnt = %d", cnt++);
    17                 write(fd, buf, len);
    18 
    19                 len = read(fd, buf, 500);
    20                 buf[len] = '';
    21                 printf("%s
    ", buf);
    22 
    23                 sleep(5);
    24         }
    25 
    26         return NULL;
    27 }
    28 
    29 int main()
    30 {
    31         int ret;
    32         int sockets[2];
    33         int bufferSize = SOCKET_BUFFER_SIZE;
    34         pthread_t thread;
    35 
    36         ret = socketpair(AF_UNIX, SOCK_SEQPACKET, 0, sockets);
    37         if(ret == -1) {
    38                 printf("socketpair create error!
    ");
    39                 return -1;
    40         }
    41 
    42         setsockopt(sockets[0], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    43         setsockopt(sockets[0], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    44         setsockopt(sockets[1], SOL_SOCKET, SO_SNDBUF, &bufferSize, sizeof(bufferSize));
    45         setsockopt(sockets[1], SOL_SOCKET, SO_RCVBUF, &bufferSize, sizeof(bufferSize));
    46 
    47         pthread_create(&thread, NULL, thread_function, (void *)(&sockets[1]));
    48 
    49         int len = 0;
    50         int fd = sockets[0];
    51         char buf[500];
    52         int cnt = 0;
    53 
    54         while(1) {
    55                 len = read(fd, buf, 500);
    56                 buf[len] = '';
    57                 printf("%s
    ", buf);
    58 
    59                 len = sprintf(buf, "Hi, thread process, cnt = %d", cnt++);
    60                 write(fd, buf, len);
    61         }
    62         return 0;
    63 }
    socketpair

     四、套接口函数

    当用于Unix套接口时,套接口函数有一些差别和限制。需注意的是并不是所有的实现中都做到了这一级。

    1. bind建立的路径名的缺省访问权限应为0777,并被当前的umask值修改。
    2. 与Unix域套接口相关联的路径名应为一个绝对路径名,而不是相对路径名。避免使用后者的原因是它依赖于调用者的当前工作目录。
    3. connect使用的路径名必须是一个绑定的某个已打开的Unix域套接口上的路径名,而且套接口的类型也必须一致。
    4. 用connect连接Unix域套接口时的权限检查和用open以只写方式访问路径名时完全相同。
    5. Unix域字节流套接口和TCP套接口类似:它们都为进程提供一个没有记录边界的字节流接口
    6. 如果Unix域字节流套接口的connect调用发现监听套接口的队列已满,会立刻返回一个ECONNREFUSED错误。如果监听套接口的队列已满,它将忽略到来的SYN,TCP连接的发起方会接着发送几次SYN重试。
    7. Unix域数据报套接口和UDP套接口类似,他们都提供一个保留记录边界的不可靠的数据报服务。
    8. 与UDP套接口不同的是,在未绑定的Unix域套接口上发送数据报不会给它绑定一个路径名。

    五、描述字传递

    从考虑从一个进程向另一个进程传递打开的描述字时,我们通常会想到:

    1. 在fork调用后,子进程共享父进程的所有打开的描述字
    2. 在调用exec时所有描述字仍保持打开

    第一个例子中进程打开一个描述字,调用fork,然后父进程关闭描述字,让子进程处理这个描述字。

    这样将一个打开的描述字从父进程传递到子进程。但我们也想让子进程打开一个描述字并捡起传递给父进程。

    1. 创建一个字节流的或数据报的Unix域套接口。
    2. 进程可以用任何返回描述字的Unix函数打开一个描述字。
    3. 发送进程建立一个msghdr接口,其中包含要传递的描述字
    4. 接收进程调用recvmsg在Unix域套接口上接收描述字。

     两个进程用socketpair通信文件描述符,并对打开的文件写入的例子:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <iostream>
    
    using namespace std;
    
    #include <sys/socket.h>
    #include <unistd.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    #include <sys/uio.h>
    
    void send_fd(int sock, int sendfd)
    {
            struct msghdr msg;
            struct iovec iov[1];
            char buf[32];
            int cmsgsize = CMSG_LEN(sizeof(int));
            struct cmsghdr *cmptr;
    
            cmptr = (cmsghdr *)malloc(cmsgsize);
            if(cmptr == NULL) {
                    cout<<"[send_fd] init cmptr error"<<endl;
                    exit(1);
            }
    
            cmptr->cmsg_level = SOL_SOCKET;
            cmptr->cmsg_type = SCM_RIGHTS;  /* send file description */
            cmptr->cmsg_len = cmsgsize;
            *((int *)CMSG_DATA(cmptr)) = sendfd;
    
            iov[0].iov_base = buf;
            iov[0].iov_len = sizeof(buf);
    
            msg.msg_name = NULL;
            msg.msg_namelen = 0;
            msg.msg_iov = iov;
            msg.msg_iovlen = 1;
            msg.msg_control = cmptr;
            msg.msg_controllen = cmsgsize;
    
            int ret = sendmsg(sock, &msg, 0);
            free(cmptr);
            if(ret == -1) {
                    cout<<"[send_fd] sendmsg error"<<endl;
                    exit(1);
            }
    }
    
    int recv_fd(int sock)
    {
            struct msghdr msg;
            struct iovec iov[1];
            char buf[32];
            int cmsgsize = CMSG_LEN(sizeof(int));
            struct cmsghdr *cmptr;
    
            iov[0].iov_base = buf;
            iov[0].iov_len = sizeof(buf);
    
    //      cmptr = CMSG_FIRSTHDR(&msg);搞不明白为什么用这个函数不行
            cmptr = (cmsghdr *)malloc(cmsgsize);
            if(cmptr == NULL) {
                    cout<<"[send_fd] init cmptr error"<<endl;
                    exit(1);
            }
    
            msg.msg_name = NULL;
            msg.msg_namelen = 0;
            msg.msg_iov = iov;
            msg.msg_iovlen = 1;
            msg.msg_control = cmptr;
            msg.msg_controllen = cmsgsize;
    
            int ret = recvmsg(sock, &msg, 0);
            free(cmptr);
            if(ret == -1) {
                    cout<<"[recv_fd] recvmsg error"<<endl;
                    exit(1);
            }
    
            int fd = *((int *)CMSG_DATA(cmptr));
            return fd;
    }
    
    void worker_process_cycle(int sockets[2])
    {
            cout<<"worker process #"<<getpid()<<endl;
            int fd = sockets[1];
            int file = recv_fd(fd);
            if(file < 0) {
                    cout<<"[worker] invalid fd!"<<endl;
                    exit(1);
            }
            char msg[] = "child process";
            cout<<"[worker] write file #"<<file<<" ret = "
                    <<write(file, msg, sizeof(msg))<<endl;
    
            close(file);
            exit(0);
    }
    
    void master_process_cycle(int sockets[2])
    {
            cout<<"master process #"<<getpid()<<endl;
            int fd = sockets[0];
    
            system("rm -f ./newfile");
            int file = open("./newfile", O_CREAT|O_TRUNC|O_RDWR);
            cout<<"[master] dispath fd to worker process, file=#"<<file<<endl;
            send_fd(fd, file);
    
            int status;
            waitpid(-1, &status, 0);
            exit(0);
    }
    
    int main(int argc, char *argv[])
    {
            cout<<"current pid: "<<getpid()<<endl;
            int sockets[2];
            if(socketpair(AF_UNIX, SOCK_STREAM, 0, sockets) == -1) {
                    cout<<"failed to create domain socket by socketpair"<<endl;
                    exit(1);
            }
            cout<<"create domain socket by sockpair success"<<endl;
    
            cout<<"create process to communicate over domain socket"<<endl;
            pid_t pid = fork();
            if(pid == 0) {
                    worker_process_cycle(sockets);
            } else {
                    master_process_cycle(sockets);
            }
    
            for( ; ; ) {
                    pause();
            }
    }
    无欲速,无见小利。欲速,则不达;见小利,则大事不成。
  • 相关阅读:
    java修饰符 protect public protected
    java中interface使用
    java中super的用法
    引用的一道JAVA题目
    java中==和equals的区别(转)
    2019PHP面试题最全面归纳总结
    (一)PHP基础知识考察点
    Linux常用命令大全(非常全!!!)
    MAMP mysql无法启动 总结(以后有发现再添加)
    win 安装composer (详细教程)
  • 原文地址:https://www.cnblogs.com/ch122633/p/8527859.html
Copyright © 2020-2023  润新知