精华内容
下载资源
问答
  • Unix域协议

    2018-05-25 17:36:53
    Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法。Unix域提供两类套接字:字节流套接字和数据报套接字。使用Unix域套接字的理由:1.Unix域套接字往往比通信两端位于同一个主机的...

    Unix域协议并不是一个实际的协议族,而是在单个主机上执行客户/服务器通信的一种方法。

    Unix域提供两类套接字:字节流套接字和数据报套接字。

    使用Unix域套接字的理由:

    1.Unix域套接字往往比通信两端位于同一个主机的TCP套接字快出一倍。

    2.Unix域套接字可用于在同一个主机上的不同进程之间传递描述符。

    3.Unix域套接字较新的实现把客户的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施。

     

    Unix域用于标识客户和服务器的协议地址是普通文件系统中的路径名。

     

    Unix域套接字地址结构

    #include<sys/un.h>
    struct sockaddr_un {
        sa_family_t  sun_family;         //AF_LOCAL
        char         sun_path[104];      //路径名
    }

    存放在sun_path数组中的路径名必须以空字符结尾。

    实现提供的SUN_LEN宏以一个指向sockaddr_un结构的指针为参数并返回该结构的长度,其中包含路径名中非空字节数。

     

    Unix域套接字的bind调用

    创建一个Unix域套接字,往其上bind一个路径名,再调用getsockname输出这个绑定的路径名

    #include "unp.h"
    
    int main(int argc, char *argv[])
    {
        int sockfd;
        socklen_t len;
        struct sockaddr_un addr1, addr2;
        
        if (argc != 2)
        err_quit("usage: unixbind <pathname>");
        
        sockfd = socket(AF_LOCAL, SOCK_STREAM,0);
        
        unlink(argv[1]);            //使用bind绑定命令行参数的路径名,先用unlink删除要绑定的路径名,以防已经存在。
        
        bzero(&addr1, sizeof(addr1));
        addr1.sun_family = AF_LOCAL;
        strncpy(addr1.sun_path,argv[1],sizeof(addr1.sun_path) - 1);
        bind(sockfd, (SA *) &addr1, SUN_LEN(&addr1));
        
        len = sizeof(addr2);
        getsockname(sockfd, (SA *) &addr2, &len);   //调用getsockname取得刚绑定的路径名到addr2中。
        printf("bound name = %s, returned len = %d \n",addr2.sun_path, len);
        
        exit(0);
    }


    socketpair函数

    socketpair函数创建两个随后连接起来的套接字。

    int socketpair(int family, int type, int protocol, int sockfd[2]);

    family参数必须为AF_LOCAL,protocol参数必须为0.type参数可以是SOCK_STREAM,也可以是SOCK_DGRAM。

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

    type参数为SOCK_STREAM创建得到的为流管道,它与调用pipe创建的普通UNIX管道类似。

     

     

    使用Unix域TCP协议的回射客户/服务器程序

    先定义两个使用的路径名:

    #define UNIXSTR_PATH    "/tmp/unix.str" /* Unix domain stream */  
    #define UNIXDG_PATH     "/tmp/unix.dg"  /* Unix domain datagram */  

    服务器端:

    /* echo_localserv.c */
    #include "unp.h"
    
    int main(int argc, char *argv[])
    {
    	int listenfd, connfd;
    	pid_t childpid;
    	socklen_t clilen;
    	struct sockaddr_un cliaddr, servaddr;
    	void sig_chld(int);
    
    	listenfd = socket(AF_LOCAL,SOCK_STREAM,0);
    
    	unlink(UNIXSTR_PATH);
    	bzero(&servaddr, sizeof(servaddr));
    	servaddr.sun_family = AF_LOCAL;
    	strcpy(servaddr.sun_path, UNIXSTR_PATH);
    
    	bind(listenfd, (SA *) &servaddr, sizeof(servaddr));
    
    	listen(listenfd,LISTENQ);
    
    	signal(SIGCHLD, sig_chld);
    
    	while(1) {
    		clilen = sizeof(cliaddr);
    		if( (connfd = accept(listenfd, (SA *) &cliaddr, *clilen)) < 0) {
    			if (errno == EINTR)
    				continue;		//back to for()
    			else 
    				err_sys("accept error");
    		}
    
    		if( (childpid = fork()) == 0) {
    			close(listenfd);
    			str_echo(connfd);
    			exit(0);
    		}
    		close(connfd);
    	}
    }
    
    void sig_chld(int signo)  
    {  
        pid_t pid;  
        int stat;  
      
        /*等待所有子进程终止*/  
        while ((pid = waitpid(-1, &stat, WNOHANG)) > 0)  
            printf("child %d terminated\n", pid);  
        return;  
    }  

    客户端: 

    /* echo_localclnt.c */
    #include "unp.h"
    
    int main(int argc, char *argv[])
    {
    	int sockfd;
    	struct sockaddr_in servaddr;
    
    	sockfd = socket(AF_LOCAL, SOCK_STREAM, 0);
    
    	bzero(&servaddr, sizeof(servaddr));
    	servaddr.sun_family = AF_LOCAL;
    	strcpy(servaddr.sun_path, UNIXSTR_PATH);
    
    	connect(sockfd, (SA *) &servaddr, sizeof(servaddr));
    
    	str_cli(stdin,sockfd);	//do it all 
    
    	exit(0);
    }

     

    使用Unix域UDP协议的客户/服务器程序

    服务器端:

    /* echo_unpserv.c */
    #include "unp.h"
    
    int main(int argc, char *argv[])
    {
    	int sockfd;
    	struct sockaddr_un servaddr, cliaddr;
    
    	sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0);
    
    	unlink(UNIXD_PATH);
    	bzero(&servaddr, sizeof(servaddr));
    	servaddr.sun_family = AF_LOCAL;
    	strcpy(servaddr.sun_path, UNIXDG_PATH);
    
    	bind(sockfd, (SA *) &servaddr, sizeof(servaddr));
    
    	dg_echo(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
    }

    客户端:

    /* echo_unpclnt.c */
    #include "unp.h"
    
    int main(int argc, char *argv[])
    {
    	int sockfd;
    	struct sockaddr_un cliaddr, servaddr;
    
    	sockfd = socket(AF_LOCAL, SOCK_DGRAM, 0 );
    
    	bzero(&cliaddr, sizeof(cliaddr));
    	cliaddr.sun_family = AF_LOCAL;
    	strcpy(cliaddr.sun_path, tmpnam(NULL));
    
    /*与TCP客户不同的是,当使用UNIX域数据报协议时,必须显式bind一个路径名*/  
    	bind(sockfd, (SA *) &cliaddr, sizeof(cliaddr));
    
    	bzero(&servaddr, sizeof(servaddr));
    	servaddr.sun_family = AF_LOCAL;
    	strcpy(servaddr.sun_path, UNIXDG_PATH);
    
    	dg_cli(stdin, sockfd, (SA *) &servaddr, sizeof(servaddr));
    
    	exit(0);
    }

     

    描述符传递的例子

    一个例子:mycat

    通过命令行参数取得一个路径名,打开这个文件,再把文件的内容复制到标准输出。

    调用 my_open函数,创建一个流管道,并调用fork和exec启动执行另一个程序,期待输出的文件由这个程序打开。该程序随后必须把打开的描述符通过流管道传递回父进程。

     

    下图:调用socketpair创建一个流管道后的mycat进程。我们以[0]和[1]标识socketpair返回的两个描述符。

                                                             

    mycat进程接着调用fork,子进程再调用exec执行openfile程序。父进程关闭[1]描述符,子进程关闭[0]描述符。

                                         

    mycat.c:

    #include "unp.h"
    
    int myopen(const char *, int);
    
    int main(int argc, char *argv[])
    {
    	int fd, n;
    	char buf[BUFFSIZE];
    
    	if (argc != 2)
    		err_quit("usage: mycat <pathname>");
    
    	if ((fd = my_open(argv[1], O_RDONLY)) < 0 )
    		err_sys("cannot open %s",argv[1]);
    
    	while ( (n = read(fd, buff, BUFFSIZE)) > 0)
    		write(STDOUT_FILENO, buff, n);
    
    	exit(0);
    }
    
    my_open(const char *pathname, int mode)
    {
    	int fd, sockfd[2], status;
    	pid_t childpid;
    	char c, argsockfd[10], argmode[10];
    
    	socketpair(AF_LOCAL, SOCK_STREAM, 0, sockfd);1
    
    	if ( (childpid = fork()) == 0) {
    		close(sockfd[0]);
    		snprintf(argcsockfd, sizeof(argsockfd), "%d", sockfd[1]);
    		snprintf(argmode, sizeof(argmode), "%d", mode);
    		excel("./openfile","openfile",argsockfd,pathname,argmode,(char *) NULL);
    		err_sys("excel error");
    	}
    
    	/* parent process - wait for the child to terminate */
    	close(sockfd[1]);
    
    	waitpid(childpid, *status, 0);
    	if (WIFEXITED(status) == 0)
    		err_quit("child did not terminate");
    	if (( status = WEXITSTATUS(status)) == 0)
    		read_fd(sock[0], &c, 1, &fd);
    	else {
    		errno = status;
    		fd = -1;
    	}
    
    	close(sockfd[0]);
    	return(fd);
    }

    展开全文
  • 第一部分 TCP事务协议 第1章 T/TCP概述 第2章 T/TCP协议 第3章 T/TCP使用举例 …… 第二部分 TCP的其他应用 ...第18章 Unix域协议:I/O和描述符的传递 附录A 测量网络时间 附录B 编写T/TCP应用程序 参考文献
  • 事务协议的发展历史,概略叙述了现有的T / T C P实现。 本书全文以及有关T / T C P的文献中,事务一词的含义都是指客户向服务器发出一个请求, 然后服务器对该请求作出应答。I n t e r n e t中最常见的一个例子是,...
  • UNIX域协议,这些协议提供了进程之间通信的一种手段。当客户与服务器进程在同一台主机上时,UNIX域协议通常要比TCP/IP快一倍。《TCP/IP详解·卷3:TCP事务协议、HTTP、NNTP和UNIX域协议》同样采用了大量的实例和实现...
  • 《TCP/IP详解(卷3):TCP事务协议、HTTP、NNTP和UNIX域协议》同样采用了大量的实例和实现细节,并参考引用了卷2中的大量源程序。《TCP/IP详解(卷3):TCP事务协议、HTTP、NNTP和UNIX域协议》适用于希望理解TCP/IP如何...
  • 对TCP/IP协议的详细讲解,此书分三个系列,这是第三卷:TCP事务协议,HTTP,NNTP和UNIX域协议
  • LINUX学习:UNIX域协议

    2019-09-26 02:19:24
    UNIX域套接字与TCP套接字相比较,在同一台主机的传输四度前者是后者的两倍 2.UNIX域套接字可以在同一台主机上各进程间传递描述符 3.UNIX域套接字与传统套接字的区别是用路径名来表示协议族的描述。 UNIX域地址结构 #...

    前言介绍:

    1。UNIX域套接字与TCP套接字相比较,在同一台主机的传输四度前者是后者的两倍

    2.UNIX域套接字可以在同一台主机上各进程间传递描述符

    3.UNIX域套接字与传统套接字的区别是用路径名来表示协议族的描述。

     

    UNIX域地址结构

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

     

    下面 我们写一个简单的回射服务器来练习一下

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <errno.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/un.h>
    #define ERR_EXIT(m) \
        do { \
            perror(m);\
            exit(EXIT_FAILURE);\
        }while(0)
    
    void echo_srv(int conn)
    {
        char recvbuf[1024];
        int ret;
        while(1)
        {
            memset(recvbuf, 0, sizeof(recvbuf));
            ret = read(conn, recvbuf, 1024);
            if(ret ==-1)
            {
                if(errno == EINTR)
                    continue;
                ERR_EXIT("read");
            }else if(ret == 0)
            {
                close(conn);
                printf("client close\n");
            }
            fputs(recvbuf, stdout);
            write(conn, recvbuf, ret);
        }
    
    
    int main(int argc, const char *argv[])
    {
        //使用UNIX域套接字,在申请套接字时就与TCP不同
        // 需要使用一个PF_UNIX的宏,而不是pf_inet
        int listenfd = socket(PF_UNIX, SOCK_STREAM, 0);
        if(listenfd < 0)
            ERR_EXIT("SoCKET");
    
        struct sockaddr_un servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sun_family = AF_UNIX;
        strcpy(servaddr.sun_path, "test_socket");
    
        if(bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");
        if(listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");
    
        int conn;
        pid_t pid;
        while(1)
        {
            conn = accpet(listenfd, NULL, NULl);
            if(conn == -1)
            {
                if(errno == EINTR)
                    continue;
                ERR_EXIT("accept");
            }
    
            pid = fork();
            if(pid == -1)
                ERR_EXIT("fork");
            if(pid == 0)
            {
                close(listenfd);    
                echo_srv(conn);
                exit(EXIT_SUCCESS);
            }else if(pid > 0)
            {
                close(conn);
    
            }
    
        }
        return 0
    }

    注意:

    1.bind成功将会创建一个文件, 权限为0777, & umask = 755

    2.sun_path最好用一个绝对路径

    3.UNIX域协议支持流式套接字与报式套接口

    4.UNIX与流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED, 这和TCP不同

    如果监听队列满, 会忽略到来的SYN, 这导致对方重传SYN;

    转载于:https://www.cnblogs.com/DLzhang/p/4303018.html

    展开全文
  • TCPIP详解卷卷三:TCP事务协议HTTPNNTP和UNIX域协议pdfTCPIP详解卷卷三:TCP事务协议HTTPNNTP和UNIX域协议pdf
  • TCP:IP详解+卷3:TCP事务协议、HTTP、NNTP和UNIX域协议.pdf 个人收集电子书,仅用学习使用,不可用于商业用途,如有版权问题,请联系删除!
  • tcp_ip详解 卷3:tcp事务协议,http,nntp和unix域协议》.pdf
  • UNIX域协议

    2014-05-15 16:20:19
    UNIX域协议支持流式套接口与报式套接口 UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,会忽略到来的SYN,这导致对方重传SYN echocli.c...

    UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。
    UNIX域套接字可以在同一台主机上各进程之间传递描述符。
    UNIX域套接字与传统套接字的区别是用路径名来表示协议族的描述。
    UNIX 域地址结构
    #define UNIX_PATH_MAX    108
    struct sockaddr_un {
    sa_family_t sun_family;               /* AF_UNIX */
    char sun_path[UNIX_PATH_MAX];  /* pathname */
    };
    bind成功将会创建一个文件,权限为0777 & ~umask
    sun_path最好用一个绝对路径
    UNIX域协议支持流式套接口与报式套接口
    UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,会忽略到来的SYN,这导致对方重传SYN


    echocli.c

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>

    #define ERR_EXIT(m) \
            do \
            { \
                    perror(m); \
                    exit(EXIT_FAILURE); \
            } while(0)

    void echo_cli(int sock)
    {
        char sendbuf[1024] = {0};
        char recvbuf[1024] = {0};
        while (fgets(sendbuf, sizeof(sendbuf), stdin) != NULL)
        {
            write(sock, sendbuf, strlen(sendbuf));
            read(sock, recvbuf, sizeof(recvbuf));
            fputs(recvbuf, stdout);
            memset(sendbuf, 0, sizeof(sendbuf));
            memset(recvbuf, 0, sizeof(recvbuf));
        }
        close(sock);
    }

    int main(void)
    {
        int sock;
        if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
            ERR_EXIT("socket");

        struct sockaddr_un servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sun_family = AF_UNIX;
        strcpy(servaddr.sun_path, "/tmp/test_socket");

        if (connect(sock, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("connect");

        echo_cli(sock);
        return 0;
    }

    echosrv.c

    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <errno.h>
    #include <string.h>

    #define ERR_EXIT(m) \
            do \
            { \
                    perror(m); \
                    exit(EXIT_FAILURE); \
            } while(0)

    void echo_srv(int conn)
    {
        char recvbuf[1024];
        int n;
        while (1)
        {
            memset(recvbuf, 0, sizeof(recvbuf));
            n = read(conn, recvbuf, sizeof(recvbuf));
            if (n == -1)
            {
                if (n == EINTR)
                    continue;

                ERR_EXIT("read");
            }
            else if (n == 0)
            {
                printf("client close\n");
                break;
            }

            fputs(recvbuf, stdout);
            write(conn, recvbuf, strlen(recvbuf));
        }

        close(conn);
    }

    int main(void)
    {
        int listenfd;
        if ((listenfd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0)
            ERR_EXIT("socket");

        unlink("/tmp/test_socket");
        struct sockaddr_un servaddr;
        memset(&servaddr, 0, sizeof(servaddr));
        servaddr.sun_family = AF_UNIX;
        strcpy(servaddr.sun_path, "/tmp/test_socket");
        
        if (bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0)
            ERR_EXIT("bind");

        if (listen(listenfd, SOMAXCONN) < 0)
            ERR_EXIT("listen");

        int conn;
        pid_t pid;
        while (1)
        {
            conn = accept(listenfd, NULL, NULL);
            if (conn == -1)
            {
                if (conn == EINTR)
                    continue;
                ERR_EXIT("accept");
            }

            pid = fork();
            if (pid == -1)
                ERR_EXIT("fork");

            if (pid == 0)
            {
                close(listenfd);
                echo_srv(conn);
                exit(EXIT_SUCCESS);
            }

            close(conn);
            
        }
        return 0;
    }

    makefile:

    .PHONY:clean all
    CC=gcc
    CFLAGS=-Wall -g
    BIN=echosrv echocli
    all:$(BIN)
    %.o:%.c
        $(CC) $(CFLAGS) -c $< -o $@
    clean:
        rm -f *.o $(BIN)


    展开全文
  • Unix域协议简介

    千次阅读 2014-07-11 21:43:55
    已经不记得什么时候第一次接触Unix Domain Socket(下文简称UDS),在我印象中,所谓UDS基本等同于本地环回接口(lo)上的TCP或者UDP,而事实上UDS所用的API也确实是套接字API。也许正因为这些先入为主的观点,自从...

    前言

    已经不记得什么时候第一次接触Unix Domain Socket(下文简称UDS),在我印象中,所谓UDS基本等同于本地环回接口(lo)上的TCP或者UDP,而事实上UDS所用的API也确实是套接字API。也许正因为这些先入为主的观点,自从进入后台开发这个领域,基本没有正眼瞧过UDS,觉得不过是单机版的TCP/UDP而已。

    最近shane兄的《OIDBset实践和技术方案》在KM上引发了激烈的讨论,当时不少人提到可以借助eventfd执行事件通知,事后我想实际做一些性能评测时,却遇到一个比较棘手的问题:eventfd创建出来的“文件描述符”在无父子关系的多进程环境中该如何共享呢?大家熟知的pipe有所谓的“命名管道”,可eventfd没有这概念。后来jayyi提到可以借助Unix域协议传递文件描述符,从那一刻起,我才决定好好研究一下这个“最熟悉的陌生人”。

    概述

    UDS提供两类套接字:字节流套接字(类似TCP)和数据报套接字(类似UDP)。根据《UNIX网络编程卷1》描述,使用UDS有以下3个理由:

    1.         在源自Berkeley的实现中,Unix域套接字往往比通信两端位于同一个主机的TCP套接字快出一倍

    2.         Unix域套接字可用于在同一个主机上的不同进程之间传递描述符

    3.         Unix域套接字较新的实现把客户的凭证(用户ID和组ID)提供给服务器,从而能够提供额外的安全检查措施

    字节流模式

    /*
    * unix_stream_server.c
    */
    
    #define _GNU_SOURCE //for struct ucred
    
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdint.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <sys/types.h>
    
    #define SERVER_PATH "/tmp/stream_server.unix"
    
    static int ConnHandler(int fd)
    {
        struct ucred credentials;
        socklen_t ucred_length = sizeof(credentials);
        if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length) < 0) {
            perror("getsockopt fail.");
            return -1;
        }
        printf("[CONN]pid=%d uid=%d gid=%d\n", (int)credentials.pid, (int)credentials.uid, (int)credentials.gid);
    
        char buffer[65535];
        uint64_t total = 0;
        int readn = 0;
        while ((readn = read(fd, buffer, sizeof(buffer))) > 0) {
            total += readn;
        }
        if (readn == 0) {
            printf("client close conn.\n");
        } else {
            perror("read fail.");
        }
        printf("total=%lu\n", total);
        close(fd);
        return 0;
    }
    
    int main(void)
    {
        unlink(SERVER_PATH);
    
        int fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if (fd < 0) {
            perror("open socket fail.");
            return -1;
        } 
    
        struct sockaddr_un local_addr;
        memset(&local_addr, 0, sizeof(local_addr));
        local_addr.sun_family = AF_UNIX;
        snprintf(local_addr.sun_path, sizeof(local_addr.sun_path), SERVER_PATH);
        if (bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
            perror("bind fail.");
            return -1;
        }
        if (listen(fd, 0) < 0) {
            perror("listen fail.");
            return -1;
        }
    
        while (1) {
            struct sockaddr_un client_addr;
            socklen_t client_addr_len = sizeof(client_addr);
            int conn_fd = accept(fd, (struct sockaddr *)&client_addr, &client_addr_len);
            if (conn_fd < 0) {
                perror("accept fail.");
                return -1;
            }
            pid_t child = fork();
            if (child == 0) {
                return ConnHandler(conn_fd);
            }
            close(conn_fd);
        }
    
        return 0;
    } 
    /*
    * unix_stream_client.c
    */
    
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdint.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <sys/types.h>
    
    #define SERVER_PATH "/tmp/stream_server.unix"
    
    int main(int argc, char *argv[])
    {
        uint64_t send_size = 1024;
        if (argc > 1) {
            send_size = strtoull(argv[1], NULL, 0);
        }
        printf("send_size=%lu\n", send_size);
    
        int fd = socket(PF_UNIX, SOCK_STREAM, 0);
        if(fd < 0) {
            perror("open socket fail.");
            return -1;
        } 
    
        struct sockaddr_un server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sun_family = AF_UNIX;
        snprintf(server_addr.sun_path, sizeof(server_addr.sun_path), SERVER_PATH);
        if (connect(fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
            perror("connect fail.");
            return -1;
        }
    
        char buffer[65535];
        uint64_t left = send_size;
        while (left > 0) {
            int writen = write(fd, buffer, left > sizeof(buffer) ? sizeof(buffer) : left);
            if (writen <= 0) {
                perror("write fail.");
                return -1;
            }
            left -= writen;
        }
        printf("write succ.\n");
    
        return 0;
    }

    注意点:

    1.         UDS捆绑路径必须使用绝对路径,POSIX声称使用相对路径将导致不可预测的结果

    2.         UDS捆绑路径若已存在,bind会失败,所以必须先调用unlink删除路径

    3.         UDS字节流套接字connect时,如果监听套接字的队列已满,会返回失败

    数据报模式

    /*
    * unix_dgram_server.c
    */
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    #include <sys/types.h>
    
    #define SERVER_PATH "/tmp/dgram_server.unix"
    
    int main(void)
    {
        unlink(SERVER_PATH);
    
        int fd = socket(PF_UNIX, SOCK_DGRAM, 0);
        if(fd < 0) {
            perror("open socket fail.");
            return -1;
        } 
    
        struct sockaddr_un local_addr;
        memset(&local_addr, 0, sizeof(local_addr));
        local_addr.sun_family = AF_UNIX;
        snprintf(local_addr.sun_path, sizeof(local_addr.sun_path), SERVER_PATH);
        if(bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
            perror("bind fail.");
            return -1;
        }
    
        while (1) {
            char buffer[65535];
            struct sockaddr_un peer_addr;
            socklen_t peer_len = sizeof(peer_addr);
            int recvn = recvfrom(fd, buffer, sizeof(buffer), 0, (struct sockaddr *)&peer_addr, &peer_len);
            if (recvn < 0) {
                perror("recvfrom fail.");
                continue;
            }
            int sendn = sendto(fd, buffer, recvn, 0, (struct sockaddr *)&peer_addr, peer_len);
            if (sendn != recvn) {
                perror("sendto fail.");
                continue;
            }
        }
    
        return 0;
    } 
    /*
    * unix_dgram_client.c
    */
    #include <stdio.h>
    #include <unistd.h>
    #include <string.h>
    #include <stdlib.h>
    #include <sys/socket.h>
    #include <sys/un.h>
    
    #define SERVER_PATH "/tmp/dgram_server.unix"
    
    int main(int argc, char *argv[])
    {
        int loop_size = 1024, loop_num = 1;
        if (argc > 1) {
            loop_size = strtoul(argv[1], NULL, 0);
        }
        if (argc > 2) {
            loop_num = strtoul(argv[2], NULL, 0);
        }
        printf("loop_size=%d loop_num=%d\n", loop_size, loop_num);
    
        int fd = socket(PF_UNIX, SOCK_DGRAM, 0);
        if(fd < 0) {
            perror("open socket fail.");
            return -1;
        } 
    
        struct sockaddr_un local_addr;
        memset(&local_addr, 0, sizeof(local_addr));
        local_addr.sun_family = AF_UNIX;
        snprintf(local_addr.sun_path, sizeof(local_addr.sun_path), tmpnam(NULL));
        if(bind(fd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0) {
            perror("bind fail.");
            return -1;
        }
    
        struct sockaddr_un server_addr;
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sun_family = AF_UNIX;
        snprintf(server_addr.sun_path, sizeof(server_addr.sun_path), SERVER_PATH);
    
        char *buffer = (char *)malloc(loop_size);
    
        int i = 0;
        for (i = 0; i < loop_num; ++i) {
            int sendn = sendto(fd, buffer, loop_size, 0, (struct sockaddr *)&server_addr, sizeof(server_addr));
            if (sendn != loop_size) {
                perror("sendto fail.");
                continue;
            }
    
            int recvn = recvfrom(fd, buffer, loop_size, 0, NULL, NULL);
            if (recvn != sendn) {
                perror("recvfrom fail.");
                continue;
            }
        }
        printf("send succ.\n");
    
        return 0;
    }

     注意点:

    1.         UDP客户端不同,UDS客户端必须显式bind路径名(tmpnam),否则服务端无法发送应答

    2.         UDP类似,若recvfrom时提供的buffer过小,数据报文会被截断,我暂时没有找到很好的办法捕捉到这个错误,求高人指点(UDS未实现MSG_TRUNC标记)

    描述符传递

    int SendFd(int sock_fd, int send_fd)
    {
        struct msghdr msg;
        memset(&msg, 0, sizeof(msg));
    
        struct iovec io_vector;
        io_vector.iov_base = "F";
        io_vector.iov_len = 1;
        msg.msg_iov = &io_vector;
        msg.msg_iovlen = 1;
    
        char control[CMSG_SPACE(sizeof(int))] = {0};
        msg.msg_control = control;
        msg.msg_controllen = sizeof(control);
    
        struct cmsghdr *cmptr = CMSG_FIRSTHDR(&msg);
        cmptr->cmsg_level = SOL_SOCKET;
        cmptr->cmsg_type = SCM_RIGHTS;
        cmptr->cmsg_len = CMSG_LEN(sizeof(int));
        *((int *)CMSG_DATA(cmptr)) = send_fd;
    
        if (sendmsg(sock_fd, &msg, 0) < 0) {
            perror("sendmsg fail.");
            return -1;
        }
        return 0;
    } 
    int ReadFd(int sock_fd)
    {
        struct msghdr msg;
        memset(&msg, 0, sizeof(struct msghdr));
    
        char buffer[1];
        struct iovec io_vector;
        io_vector.iov_base = buffer;
        io_vector.iov_len = sizeof(buffer);
        msg.msg_iov = &io_vector;
        msg.msg_iovlen = 1;
    
        char control[CMSG_SPACE(sizeof(int))] = {0};
        msg.msg_control = control;
        msg.msg_controllen = sizeof(control);
    
        if (recvmsg(sock_fd, &msg, 0) < 0) {
            perror("recvmsg fail.");
            return -1;
        }
        if (msg.msg_flags & MSG_CTRUNC) {
            printf("control data truncated.\n");
            return -1;
        }
    
        struct cmsghdr *msptr = CMSG_FIRSTHDR(&msg);
        if (!msptr
                || msptr->cmsg_level != SOL_SOCKET
                || msptr->cmsg_type != SCM_RIGHTS
                || msptr->cmsg_len != CMSG_LEN(sizeof(int))) {
            printf("check fail.\n");
            return -1;
        }
        return *((int *)CMSG_DATA(msptr));
    }

     注意点:

    1.         这里给出的例子是基于字节流模式,其实数据报模式也可以

    2.         这里的文件描述符可以通过openpipemkfifosocket等获取,类型不限

    3.         发送进程在调用sendmsg之后立即关闭该描述符,并不影响接收进程

    客户凭证

    struct ucred credentials;
    socklen_t ucred_length = sizeof(credentials);
    if (getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &credentials, &ucred_length) < 0) {
        perror("getsockopt fail.");
        return -1;
    }
    int pid = credentials.pid, uid = credentials.uid, gid = credentials.gid;

     注意点:

    1.         使用ucred结构体定义需要添加“#define _GNU_SOURCE”,不要直接使用”#define __USE_GNU”,这是glibc的内部宏

    性能评估

    机器配置


    编译命令:gcc –g –Wall –o xxx xxx.c

    字节流UDS VS 本地环回TCP

    测试场景:ClientServer写入10GB数据量耗费时间,每次写入65535B,分多次写入

    测试结果:



     

    R1

    R2

    R3

    R4

    R5

    AVG

    UDS

    1.395

    1.732

    1.566

    1.629

    1.308

    1.526

    TCP

    2.493

    2.497

    2.959

    2.707

    2.446

    2.620

     

    数据报UDS VS 本地环回UDP

    测试场景:ClientServer写入100w大小为1KB的数据包,echo模式

    测试结果:



     

    R1

    R2

    R3

    R4

    R5

    AVG

    UDS

    9.661

    9.308

    8.669

    9.837

    8.469

    9.189

    UDP

    10.738

    11.280

    11.257

    11.480

    10.304

    11.012

    不难看出,数据报模式和本地环回UDP性能差距不大,而字节流模式和本地环回TCP性能差距还是比较大的。猜测可能原因:TCP的拥塞控制、ACK、重传等机制造成的冗余开销相比UDP更为客观,除正常的pack/unpack、校验码等因素。

    小结

    最后,聊一聊关于UDS的缓冲区问题。在摸索的过程中,层尝试Google、咨询牛人等多方无果,最后经过反复尝试,基本得出以下结论:

    1.         对于UDS而言,仅SO_SNDBUF生效,SO_RCVBUF无效,对于net.core.wmem_defaultnet.core.wmem_maxnet.core.rmem_defaultnet.core.rmem_max就不言而喻了

    2.         对于字节流模式而言,SO_SNDBUF决定了发送方的缓冲区大小,这很容易理解

    3.         对于数据报模式而言,一方面,SO_SNDBUF决定了发送方的缓冲区大小,另一方面,net.unix.max_dgram_qlen参数决定了最大报文数

    4.         UDP不同,UDP的接收缓冲区如果满了,后续的数据报会自动丢弃;而UDS的数据报模式,如果发送缓冲区达到上限,在阻塞模式下,发送方将被阻塞,非阻塞模式下也可以捕获到响应错误。个人感觉这个特性也挺赞的,可以用于dispatcher-workers模型的任务分发,per-worker-per-queuedispatcher可以非常容易感知worker的负载情况,当然是用字节流模式的同样可以感知,但是无需人为分包还是比较爽快的

    展开全文
  • 第18章 Unix域协议:I/O和描述符的传递 210 18.1 概述 210 18.2 PRU_SEND和PRU_RCVD请求 210 18.3 描述符的传递 214 18.4 unp_internalize函数 218 18.5 unp_externalize函数 220 18.6 unp_discard函数 221 18.7 unp...
  • tcp-ip详解卷1:协议.TCP-IP详解卷2:TCP-IP详解卷3:TCP事务协议,HTTP,NNTP和UNIX域协议
  • 第三卷的内容细节覆盖了当今TCP/IP编程人员和网络管理员必须熟练掌握... UNIX域协议,这是在UNIX实现中应用非常广泛的一套协议。 与前面两卷一样,本书有丰富的例子和实现的细节,他们都是4.4BSD-Lite中的网络代码。
  • TCP-IP详解卷3:TCP事务协议,HTTP,NNTP和UNIX域协议.rar,第三步。
  • TCPIP详解 经典的网络书 总共有三卷 高清晰的PDF 英译汉 中文版
  • TCP-IP详解卷3:TCP事务协议,HTTP,NNTP和UNIX域协议
  • UNIX域协议,这些协议提供了进程之间通信的一种手段。当客户与服务器进程在同一台主机上时,UNIX域协议通常要比TCP/IP快一倍。《CP.IP详解(卷3):CP事务协议.HP.P和UIX域协议》同样采用了大量的实例和实现细节,并...
  • TCP-IP详解卷3 -- tcp事务协议,http,nntp和unix域协议.pdf
  • TCP-IP_3(TCP事务协议,HTTP,NNTP和UNIX域协议).pdf
  • TCP-IP详解卷3:TCP 事务协议、HTTP、NNTP和UNIX域协议 【高清 有目录 无水印 未加密】可以打印。另有卷1、2
  • TCP-IP详解卷,全三册,包含 TCP-IP详解卷(一)协议 ;TCP-IP详解卷(二)实现;TCP-IP详解卷(三) TCP事务协议、HTTP、NNTP和UNIX域协议

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 51,414
精华内容 20,565
关键字:

unix域协议