socket IPC(本地套接字 domain)
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编译后如下图所示文件
(2)先执行./server程序,再执行./client程序
(3)在客户端输入hello world,会提示输入读写了多少字节,并且将服务端大写转小写写回到客户端屏幕上
(4)程序执行完成后会在当前目录下观察到有两个套接字文件:clie.socket和serv.socket