socket IPC(本地套接字 domain)

时间:2023-02-08 18:36:16

1.简介

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

  UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIXDomain Socket通讯的

2.使用介绍

  使用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()错误返回。

3.与网络套接字对比

  对比网络套接字地址结构和本地套接字地址结构:

  struct sockaddr_in {

  __kernel_sa_family_t sin_family;                             /* Address family */      地址结构类型

  __be16 sin_port;                                                 /* Port number */             端口号

  struct in_addr sin_addr;                                               /* Internet address */     IP地址

  };

  struct sockaddr_un {

  __kernel_sa_family_t sun_family;                  /* AF_UNIX */                    地址结构类型

  char sun_path[UNIX_PATH_MAX];                 /* pathname */                 socket文件名(含路径)

  };

4.代码实现案列

文件1:wrap.h自己封装的出错处理函数头文件

 1 #ifndef __WRAP_H_
 2 #define __WRAP_H_
 3 
 4 void perr_exit(const char* s);
 5 int Accept(int fd,struct sockaddr* sa,socklen_t* salenptr);
 6 int Bind(int fd,const struct sockaddr* sa,socklen_t salen);
 7 int Connect(int fd,const struct sockaddr* sa,socklen_t salen);
 8 int Listen(int fd,int backlog);
 9 int Socket(int family,int type,int protocol);
10 ssize_t Read(int fd,void* ptr,size_t nbytes);
11 ssize_t Write(int fd,const void* ptr,size_t nbytes);
12 int Close(int fd);
13 ssize_t Readn(int fd,void* vptr,size_t n);
14 ssize_t Writen(int fd,const void* vptr,size_t n);
15 ssize_t my_read(int fd,char* ptr);
16 ssize_t Readline(int fd,void* vptr,size_t maxlen);
17 
18 #endif

文件2:wrap.c自己封装的出错处理函数实现

  1 #include<stdlib.h>
  2 #include<stdio.h>
  3 #include<unistd.h>
  4 #include<errno.h>
  5 #include<sys/socket.h>
  6 
  7 void perr_exit(const char* s)
  8 {
  9     perror(s);
 10     exit(-1);
 11 }
 12 
 13 int Accept(int fd,struct sockaddr* sa,socklen_t* salenptr)
 14 {
 15     int n;
 16 again:
 17     if((n = accept(fd,sa,salenptr))<0)
 18     {
 19         if((errno == ECONNABORTED)||(errno == EINTR))
 20         {
 21             goto again;
 22         }
 23         else
 24         {
 25             perr_exit("accept error");
 26         }
 27     }
 28     return n;
 29 }
 30 
 31 int Bind(int fd,const struct sockaddr* sa,socklen_t salen)
 32 {
 33     int n;
 34     if((n = bind(fd,sa,salen))<0)
 35     {
 36         perr_exit("bind error");
 37     }
 38     return n;
 39 }
 40 
 41 int Connect(int fd,const struct sockaddr* sa,socklen_t salen)
 42 {
 43     int n;
 44     n = connect(fd,sa,salen);
 45     if(n<0)
 46     {
 47         perr_exit("connect error");
 48     }
 49     return n;
 50 }
 51 
 52 int Listen(int fd,int backlog)
 53 {
 54     int n;
 55     if((n = listen(fd,backlog))<0)
 56     {
 57         perr_exit("listen error");
 58     }
 59     return n;
 60 }
 61 
 62 int Socket(int family,int type,int protocol)
 63 {
 64     int n;
 65     if((n = socket(family,type,protocol))<0)
 66     {
 67         perr_exit("socket error");
 68     }
 69     return n;
 70 }
 71 
 72 ssize_t Read(int fd,void* ptr,size_t nbytes)
 73 {
 74     ssize_t n;
 75 again:
 76     if((n = read(fd,ptr,nbytes)) == -1)
 77     {
 78         if(errno == EINTR)
 79         {
 80             goto again;
 81         }
 82         else
 83         {
 84             return -1;
 85         }
 86     }
 87     return n;
 88 }
 89 
 90 ssize_t Write(int fd,const void* ptr,size_t nbytes)
 91 {
 92     ssize_t n;
 93 again:
 94     if((n = write(fd,ptr,nbytes)) == -1)
 95     {
 96         if(errno == EINTR)
 97         {
 98             goto again;
 99         }
100         else
101         {
102             return -1;
103         }
104     }
105     return n;
106 }
107 
108 int Close(int fd)
109 {
110     int n;
111     if((n = close(fd)) == -1)
112     {
113         perr_exit("close error");
114     }
115     return n;
116 }
117 
118 ssize_t Readn(int fd,void* vptr,size_t n)
119 {
120     size_t nleft;
121     ssize_t nread;
122     char *ptr;
123     
124     ptr = vptr;
125     nleft = n;
126 
127     while(nleft > 0)
128     {
129         if((nread = read(fd,ptr,nleft))<0)
130         {
131             if(errno == EINTR)
132             {
133                 nread = 0;
134             }
135             else
136             {
137                 return -1;
138             }
139         }
140         else if(nread == 0)
141         {
142             break;
143         }
144         nleft -= nread;
145         ptr += nread;
146     }
147     return n-nleft;
148 }
149 
150 ssize_t Writen(int fd,const void* vptr,size_t n)
151 {
152     size_t nleft;
153     ssize_t nwritten;
154     const char* ptr;
155 
156     ptr = vptr;
157     nleft = n;
158     while(nleft>0)
159     {
160         if((nwritten = write(fd,ptr,nleft))<=0)
161         {
162             if(nwritten < 0&& errno == EINTR)
163             {
164                 nwritten = 0;
165             }
166             else
167             {
168                 return -1;
169             }
170             nleft -= nwritten;
171             ptr += nwritten;
172         }
173     }
174     return n;
175 }
176 
177 ssize_t my_read(int fd,char* ptr)
178 {
179     static int read_cnt;
180     static char *read_ptr;
181     static char read_buf[100];
182 
183     if (read_cnt <= 0) 
184     {
185 again:
186         if ( (read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) 
187         {
188             if (errno == EINTR)
189                 goto again;
190             return -1;
191         } else if (read_cnt == 0)
192             return 0;
193         read_ptr = read_buf;
194     }
195     read_cnt--;
196     *ptr = *read_ptr++;
197 
198     return 1;
199 }
200 
201 ssize_t Readline(int fd,void* vptr,size_t maxlen)
202 {
203     ssize_t n, rc;
204     char    c, *ptr;
205     ptr = vptr;
206 
207     for (n = 1; n < maxlen; n++) 
208     {
209         if ((rc = my_read(fd, &c)) == 1)
210         {
211             *ptr++ = c;
212             if (c == '\n')
213                 break;
214         } else if (rc == 0) 
215         {
216             *ptr = 0;
217             return n-1;                                                            
218         } else                                            
219             return -1;                                                
220     }
221     *ptr = 0;
222 
223     return n;
224 }

文件3:服务端程序server.c

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <sys/types.h>
 4 #include <sys/socket.h>
 5 #include <strings.h>
 6 #include <string.h>
 7 #include <ctype.h>
 8 #include <arpa/inet.h>
 9 #include<sys/un.h>
10 #include<stdlib.h>
11 #include<stddef.h>
12 #include "wrap.h"
13 #define SERV_ADDR "serv.socket"
14 #define SERV_PORT 6666
15 
16 int main(void)
17 {
18     int lfd, cfd;
19     int len, i;
20     char buf[BUFSIZ];
21 
22     struct sockaddr_un serv_addr, clie_addr;
23     socklen_t clie_addr_len;
24 
25     lfd = Socket(AF_UNIX, SOCK_STREAM, 0);
26 
27     unlink(SERV_ADDR);
28     bzero(&serv_addr, sizeof(serv_addr));           
29     serv_addr.sun_family = AF_UNIX;                 
30     len=offsetof(struct sockaddr_un,sun_path)+strlen(SERV_ADDR);
31     strcpy(serv_addr.sun_path,SERV_ADDR);
32 
33     Bind(lfd, (struct sockaddr *)&serv_addr, len);
34 
35     Listen(lfd, 128);                                
36 
37     printf("wait for client connect ...\n");
38 
39     clie_addr_len = sizeof(clie_addr_len);
40 
41     cfd = Accept(lfd, (struct sockaddr *)&clie_addr, &clie_addr_len);
42 
43     while (1) {
44         
45         len = Read(cfd, buf, sizeof(buf));
46         Write(STDOUT_FILENO, buf, len);
47 
48         for (i = 0; i < len; i++)
49             buf[i] = toupper(buf[i]);
50         Write(cfd, buf, len); 
51     }
52 
53     Close(lfd);
54     Close(cfd);
55 
56     return 0;
57 }

文件4:客户端程序client.c

 1 #include <stdio.h>
 2 #include <unistd.h>
 3 #include <string.h>
 4 #include <sys/socket.h>
 5 #include <arpa/inet.h>
 6 #include <stdlib.h>
 7 #include <stddef.h>
 8 #include <sys/un.h>
 9 #include <ctype.h>
10 #include <sys/types.h>
11 #include "wrap.h"
12 #define CLIE_ADDR "clie.socket"
13 #define SERV_ADDR "serv.socket"
14 #define SERV_PORT 6666
15 
16 int main(void)
17 {
18     int lfd, len,n;
19     struct sockaddr_un serv_addr, clie_addr;
20     char buf[BUFSIZ]; 
21 
22     lfd = Socket(AF_UNIX, SOCK_STREAM, 0);
23 
24     bzero(&clie_addr, sizeof(clie_addr));                       
25     clie_addr.sun_family = AF_UNIX;                                 
26     strcpy(clie_addr.sun_path,CLIE_ADDR);
27     len=offsetof(struct sockaddr_un,sun_path)+strlen(CLIE_ADDR);
28     unlink(CLIE_ADDR);
29     Bind(lfd,(struct sockaddr*)&clie_addr,len);
30     
31     bzero(&serv_addr,sizeof(serv_addr));
32     serv_addr.sun_family=AF_UNIX;
33     strcpy(serv_addr.sun_path,SERV_ADDR);
34     len=offsetof(struct sockaddr_un,sun_path)+strlen(SERV_ADDR);
35 
36     Connect(lfd, (struct sockaddr *)&serv_addr, len);
37 
38     while (1) {
39         fgets(buf, sizeof(buf), stdin);
40         int r = Write(lfd, buf, strlen(buf));       
41         printf("Write r ======== %d\n", r);
42         n = Read(lfd, buf, sizeof(buf));
43         printf("Read len ========= %d\n", n);
44         Write(STDOUT_FILENO, buf, n);
45     }
46 
47     Close(lfd);
48 
49     return 0;
50 }

文件5:makefile编译文件

 1 src = $(wildcard *.c)
 2 obj = $(patsubst %.c,%.o,$(src))
 3 
 4 all:server client
 5 server:server.o wrap.o
 6     gcc server.o wrap.o -o server -Wall
 7 client:client.o wrap.o
 8     gcc client.o wrap.o -o client -Wall
 9 %.o:%.c
10     gcc -c $< -Wall
11 .PHONY:clean all
12 clean:
13     -rm -rf server client $(obj)

5.编译以及运行效果

(1)执行make编译后如下图所示文件

socket IPC(本地套接字 domain)

(2)先执行./server程序,再执行./client程序

socket IPC(本地套接字 domain)

socket IPC(本地套接字 domain)

(3)在客户端输入hello world,会提示输入读写了多少字节,并且将服务端大写转小写写回到客户端屏幕上

socket IPC(本地套接字 domain)

socket IPC(本地套接字 domain)

(4)程序执行完成后会在当前目录下观察到有两个套接字文件:clie.socket和serv.socket

socket IPC(本地套接字 domain)