《UNIX环境高级编程》笔记--UNIX域套接字

来源:互联网 发布:程序员健康指南 编辑:程序博客网 时间:2024/06/05 04:20

一、UNIX Domain Socket IPC      
  

socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。UNIX域套接字与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。UNIX Domain Socket也提供面向流和面向数据包两种API接口,类似于TCP和UDP,但是面向消息的UNIXDomain Socket也是可靠的,消息既不会丢失也不会顺序错乱。  

    使用UNIX Domain Socket的过程和网络socket十分相似,也要先调用socket()创建一个socket文件描述符,address family指定为AF_UNIX,type可以选择SOCK_DGRAM或SOCK_STREAM,protocol参数仍然指定为0即可。    
  

UNIX Domain Socket与网络socket编程最明显的不同在于地址格式不同,用结构体sockaddr_un表示,网络编程的socket地址是IP地址加端口号,而UNIX Domain Socket的地址是一个socket类型的文件在      文件系统中的路径      ,这个socket文件由bind()调用创建,如果调用bind()时该文件已存在,则bind()错误返回。      

#define UNIX_PATH_MAX    108      

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

二、回射/客户服务器程序  

通信的流程跟前面说过的tcp/udp 是类似的,下面直接来看程序:  

 C++ Code 
1
                2                
                3                
                4                
                5                
                6                
                7                
                8                
                9                
10
                11                
                12                
                13                
                14                
                15                
                16                
                17                
                18                
                19                
                20                
21
                22                
                23                
                24                
                25                
                26                
                27                
                28                
                29                
                30                
                31                
                32                
                33                
                34                
                35                
                36                
                37                
                38                
                39                
                40                
                41                
                42                
                43                
                44                
                45                
                46                
                47                
                48                
                49                
                50                
                51                
                52                
                53                
                54                
                55                
                56                
                57                
                58                
                59                
                60                
                61                
                62                
                63                
                64                
                65                
                66                
                67                
                68                
                69                
                70                
                71                
                72                
                73                
                74                
                75                
                76                
                77                
                78                
                79                
                80                
                81                
                82                
                83                
                84                
                85                
                86                
                87                
                88                
                89                
                90                
                91                
                92                
                93                
                94                
                95                
                96                
                97                
                98                
                99                
                100                
                101                
                102                
            
                /*************************************************************************                
                    > File Name: echoser_tcp.c                
                    > Author: Simba                
                    > Mail: dameng34@163.com                
                    > Created Time: Sun 03 Mar 2013 06:13:55 PM CST                
                 ************************************************************************/              
              
#include              <stdio.h>              
#include              <stdlib.h>              
#include              <unistd.h>              
#include              <errno.h>              
#include              <sys/types.h>              
#include              <sys/socket.h>              
#include              <netinet/in.h>              
#include              <string.h>              
#include              <sys/un.h>              

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

void               echo_ser(               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 error"              );              
                      }              

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

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

                  close(conn);              
              }              

/* unix domain socket与TCP套接字相比较,在同一台主机的传输速度前者是后者的两倍。*/              
int               main(               void              )              
              {              
                                 int               listenfd;              
                                 if               ((listenfd = socket(PF_UNIX, SOCK_STREAM,               0              )) <                0              )              
                      ERR_EXIT(               "socket error"              );              

                  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 error"              );              

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

                                 int               conn;              
                  pid_t pid;              

                                 while               (              1              )              
                  {              

                      conn = accept(listenfd,                NULL              ,                NULL              );              
                                     if               (conn == -              1              )              
                      {              

                                         if               (conn == EINTR)              
                                             continue              ;              
                          ERR_EXIT(               "accept error"              );              
                      }              

                      pid = fork();              
                                     if               (pid == -              1              )              
                          ERR_EXIT(               "fork error"              );              
                                     if               (pid ==               0              )              
                      {              
                          close(listenfd);              
                          echo_ser(conn);              
                          exit(EXIT_SUCCESS);              
                      }              

                      close(conn);              
                  }              

                                 return                             0              ;              
              }            

 C++ Code 
                1                
                2                
                3                
                4                
                5                
                6                
                7                
                8                
                9                
                10                
                11                
                12                
                13                
                14                
                15                
                16                
                17                
                18                
                19                
                20                
                21                
                22                
                23                
                24                
                25                
                26                
                27                
                28                
                29                
                30                
                31                
                32                
                33                
                34                
                35                
                36                
                37                
                38                
                39                
                40                
                41                
                42                
                43                
                44                
                45                
                46                
                47                
                48                
                49                
                50                
                51                
                52                
                53                
                54                
                55                
                56                
                57                
                58                
                59                
                60                
                61                
                62                
                63                
                64                
            
                /*************************************************************************                
                    > File Name: echocli_tcp.c                
                    > Author: Simba                
                    > Mail: dameng34@163.com                
                    > Created Time: Sun 03 Mar 2013 06:13:55 PM CST                
                 ************************************************************************/              
              
#include              <stdio.h>              
#include              <stdlib.h>              
#include              <unistd.h>              

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

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

void               echo_cli(               int               conn)              
              {              
                                 char               sendbuf[              1024              ] = {               0              };              
                                 char               recvbuf[              1024              ] = {               0              };              
                                 while               (fgets(sendbuf,               sizeof              (sendbuf), stdin) !=               NULL              )              
                  {              

                      write(conn, sendbuf, strlen(sendbuf));              
                      read(conn, recvbuf,                sizeof              (recvbuf));              
                      fputs(recvbuf, stdout);              
                      memset(recvbuf,                0              ,               sizeof              (recvbuf));              
                      memset(sendbuf,                0              ,               sizeof              (sendbuf));              
                  }              

                  close(conn);              
              }              

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

                                 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 error"              );              

                  echo_cli(sock);              

                                 return                             0              ;              
              }            

server 使用fork 的形式来接受多个连接,server调用bind 会创建一个文件,如下所示:  

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ls -l /tmp/test\ socket       
      srwxrwxr-x 1 simba simba 0 Jun 12 15:27 /tmp/test socket      
  

即文件类型为s,表示SOCKET文件,与FIFO(命名管道)文件,类型为p,类似,都表示内核的一条通道,读写文件实际是在读写内核通道。程序中调用unlink 是为了在开始执行程序时删除以前创建的文件,以便在重启服务器时不会提示address in use。其他方面与以前说过的回射客户服务器程序没多大区别,不再赘述。  

三、UNIX域套接字编程注意点  

1、bind成功将会创建一个文件,权限为0777 & ~umask      
      2、sun_path最好用一个绝对路径      
3、UNIX域协议支持流式套接口与报式套接口      
4、UNIX域流式套接字connect发现监听队列满时,会立刻返回一个ECONNREFUSED,这和TCP不同,如果监听队列满,会忽略到来的SYN,这导致对方重传SYN。      
  

  

四、socketpair 函数  

      功能:创建一个全双工的流管道      
      原型       int socketpair(int domain, int type, int protocol, int sv[2]);      
参数      
domain: 协议家族      
type: 套接字类型      
protocol: 协议类型      
sv: 返回套接字对      
      返回值:成功返回0;失败返回-1      
  

实际上socketpair 函数跟pipe 函数是类似的,也只能在同个主机上具有亲缘关系的进程间通信,但pipe 创建的匿名管道是半双工的,而socketpair 可以认为是创建一个全双工的管道。  

可以使用socketpair 创建返回的套接字对进行父子进程通信:  

 C++ Code 
                1                
                2                
                3                
                4                
                5                
                6                
                7                
                8                
                9                
                10                
                11                
                12                
                13                
                14                
                15                
                16                
                17                
                18                
                19                
                20                
                21                
                22                
                23                
                24                
                25                
                26                
                27                
                28                
                29                
                30                
                31                
                32                
                33                
                34                
                35                
                36                
                37                
                38                
                39                
                40                
                41                
                42                
                43                
                44                
                45                
                46                
                47                
                48                
                49                
                50                
                51                
                52                
                53                
                54                
                55                
                56                
                57                
                58                
                59                
                60                
                61                
                62                
                63                
                64                
                65                
                66                
                67                
                68                
                69                
            
                /*************************************************************************                
                    > File Name: echoser.c                
                    > Author: Simba                
                    > Mail: dameng34@163.com                
                    > Created Time: Fri 01 Mar 2013 06:15:27 PM CST                
                 ************************************************************************/              
              
#include              <stdio.h>              
#include              <sys/types.h>              
#include              <sys/socket.h>              
#include              <unistd.h>              
#include              <stdlib.h>              
#include              <errno.h>              
#include              <arpa/inet.h>              
#include              <netinet/in.h>              
#include              <string.h>              

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

int               main(               void              )              
              {              
                                 int               sockfds[              2              ];              

                                 if               (socketpair(PF_UNIX, SOCK_STREAM,               0              , sockfds) <                0              )              
                      ERR_EXIT(               "sockpair"              );              

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

                                 if               (pid >               0              )              
                  {              
                                     int               val =               0              ;              
                      close(sockfds[               1              ]);              
                                     while               (              1              )              
                      {              

                          ++val;              
                          printf(               " sending data: %d\n"              , val);              
                          write(sockfds[               0              ], &val,                sizeof              (val));              
                          read(sockfds[               0              ], &val,               sizeof              (val));              
                          printf(               "recv data : %d\n"              , val);              
                          sleep(               1              );              
                      }              

                  }              

                                 else                             if               (pid ==                0              )              
                  {              

                                     int               val;              
                      close(sockfds[               0              ]);              
                                     while               (              1              )              
                      {              

                          read(sockfds[               1              ], &val,               sizeof              (val));              
                          ++val;              
                          write(sockfds[               1              ], &val,                sizeof              (val));              
                      }              
                  }              

                                 return                             0              ;              
              }            

输出如下:  

simba@ubuntu:~/Documents/code/linux_programming/UNP/socket$ ./socketpair       
       sending data: 1      
      recv data : 2      
       sending data: 3      
      recv data : 4      
       sending data: 5      
      recv data : 6      
       sending data: 7      
      recv data : 8      
       sending data: 9      
      recv data : 10      
       sending data: 11      
      recv data : 12      
       sending data: 13      
      recv data : 14      
       sending data: 15      
      recv data : 16      
      ...................................    
  

  

即父进程持有sockfds[0] 套接字进行读写,而子进程       持有sockfds[1] 套接字进行读写。

 

 

1.非命名的UNIX域套接字

UNIX套接字用于在用一台机器上运行的进程之间通信。UNIX套接字比因特网域套接字的效率更高。UNIX与套接字提供和

数据报两种接口,UNIX域数据报服务是可靠的,就不会丢失消息也不会传递出错。UNIX域套接字是套接字和管道之间的混合物。

为了创建一对非命名的,相互连接的UNXI域套接字,用户可以使用socketopair函数。

 

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

//若成功则返回0,出错则返回-1.

 

#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <string.h>

int main(void){
        int fd[2];
        int pid;
        char wbuf[16] = "1234567890";
        char rbuf[16];
        if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) < 0){
                perror("socketpair");
                return -1;
        }

        if((pid = fork())<0){
                perror("fork");
                return -1;
        }else if(pid == 0){
                //child
                close(fd[0]);
                if(write(fd[1],wbuf,strlen(wbuf)) < 0){
                        perror("write");
                        exit(-1);
                }
        }else{
                //parent
                close(fd[1]);
                if(read(fd[0],rbuf,16) < 0){
                        perror("read");
                        exit(-1);
                }
                printf("%s\n",rbuf);
        }
        return 0;
}

 

执行结果:

yan@yan-vm:~$ ./a.out
1234567890


2.命名UNIX域套接字:服务端与客户端都有bind

虽然socketpair函数创建相互连接的一对套接字,但是每一个套接字都没有名字。这意味着无关进程不能使用它们。

我们可以命名unix域套接字,并可将其用于告示服务。但是要注意的是,UNXI与套接字使用的地址不同与因特网域套接字。

UNIX域套接字的地址由sockaddr_un结构表示。

在linux2.4.22中,sockaddr_un结构按下列形式定义在有文件<sys/un.h>中。

struct sockaddr_un{

sa_family_t sun_family; //AF_UNIX

char sun_path[108]; //pathname

};

sun_path成员包含一路经名,当我们将一个地址绑定至UNIX域套接字时,系统用该路经名创建一类型为S_IFSOCK文件。

该文件仅用于向客户进程告知套接字名字。该文件不能打开,也不能由应用程序用于通信。

如果当我们试图绑定地址时,该文件已经存在,那么bind请求失败。当关闭套接字时,并不自动删除该文件,所以我们必须

确保在应用程序终止前,对该文件执行解除链接操作。

服务器进程可以使用标准bind、listen和accept函数,为客户进程安排一个唯一UNIX域连接。客户进程使用connect与服务器

进程连接;服务器进程接受了connect请求后,在服务器进程和客户进程之间就存在了唯一连接。这种风格和因特网套接字

的操作很像。

实践:

server.c

#include <stdio.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <string.h>

int main(void){
        int fd,clientfd;
        int client_addr_len = sizeof(struct sockaddr_un);
        struct sockaddr_un un,clientun;
        char path[32]="serversocketfile";
        int addr_len = sizeof(struct sockaddr_un);
        char rbuf[32];
        char wbuf[32] = "i am server.";

        //create a UNIX domain stream socket
        if((fd = socket(AF_UNIX,SOCK_STREAM,0)) < 0){
                perror("socket");
                return -1;
        }

        //in case it already exists
        unlink(path);

        //fill in socket address structure
        memset(&un, 0, sizeof(un));
        un.sun_family = AF_UNIX;
        strncpy(un.sun_path,path,32);

        //bind the name to the descriptor
        if(bind(fd, (struct sockaddr*)&un, addr_len) < 0){
                perror("bind");
                return -1;
        }

        if(listen(fd, 10) < 0){
                perror("listen");
                return -1;
        }

        if((clientfd = accept(fd,(struct sockaddr*)&clientun,(socklen_t*)&client_addr_len)) < 0 ){
                perror("accept");
                return -1;
        }

        printf("client is:%s\n",clientun.sun_path);

        if(read(clientfd,rbuf,32) < 0){
                perror("read");
                return -1;
        }
        printf("receive msg:%s\n",rbuf);

        if(write(clientfd,wbuf,strlen(wbuf)+1) < 0){
                perror("write");
                return -1;
        }

        unlink(path);
        return 0;
}

client.c

#include <stdio.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <string.h>

int main(void){
        int fd;
        struct sockaddr_un un;
        char path[32] = "clientsocketfile";
        char serverpath[32] = "serversocketfile";  // 与服务端一样
        int addr_len = sizeof(struct sockaddr_un);
        char wbuf[32] = "i am client.";
        char rbuf[32];

        if((fd = socket(AF_UNIX,SOCK_STREAM,0))<0){
                perror("socket");
                return -1;
        }

        memset(&un,0,sizeof(un));
        un.sun_family = AF_UNIX;
        strncpy(un.sun_path,path,32);
        unlink(path);

        if(bind(fd,(struct sockaddr*)&un,addr_len)<0){
                perror("bind");
                return -1;
        }

        //fill socket adress structure with server's address
        memset(&un,0,sizeof(un));
        un.sun_family = AF_UNIX;
        strncpy(un.sun_path,serverpath,32);

        if(connect(fd,(struct sockaddr*)&un,addr_len) < 0){
                perror("connect");
                return -1;
        }

        if(write(fd,wbuf,strlen(wbuf)+1)<0){
                perror("write");
                return -1;
        }

        if(read(fd,rbuf,32) < 0){
                perror("write");
                return -1;
        }
        printf("receive msg:%s\n",rbuf);
        unlink(path);
        return -1;
}

 

先运行server,再运行client。

运行结果:

server.c:

yan@yan-vm:~/apue$ ./unixserver
client is:clientsocketfile
receive msg:i am client.

client.c:

yan@yan-vm:~/apue$ ./unixclient
receive msg:i am server.

 

 

0 0
原创粉丝点击