IPC-本地套接字
一。Unix domain socket概念
socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket。虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.1),但是UNIX Domain Socket用于IPC更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。
UNIX Domain Socket是全双工的,API接口语义丰富,相比其它IPC机制有明显的优越性,目前已成为使用最广泛的IPC机制,比如X Window服务器和GUI程序之间就是通过UNIXDomain Socket通讯的。
二。本地套接字地址结构
struct sockaddr_un {
__kernel_sa_family_t sun_family; /* AF_UNIX */ 地址结构类型
char sun_path[UNIX_PATH_MAX]; /* pathname */ socket文件名(含路径)
};
三。代码示例
服务端
1 #include <stdio.h> 2 #include <unistd.h> 3 #include <sys/socket.h> 4 #include <strings.h> 5 #include <string.h> 6 #include <ctype.h> 7 #include <arpa/inet.h> 8 #include <sys/un.h> 9 #include <stddef.h> 10 11 #include "wrap.h" 12 13 #define SERV_ADDR "serv.socket" 14 15 int main(void) 16 { 17 int lfd, cfd, len, size, i; 18 struct sockaddr_un servaddr, cliaddr; 19 char buf[4096]; 20 21 lfd = socket(AF_UNIX, SOCK_STREAM, 0); //本地套接字 AF_UNIX 22 23 bzero(&servaddr, sizeof(servaddr)); 24 servaddr.sun_family = AF_UNIX; //初始化地址信息 25 strcpy(servaddr.sun_path,SERV_ADDR); //套接字文件名 26 27 len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); /* servaddr total len */ 28 //offsetof计算结构体中成员的偏移量,偏移量+文件名大小=结构体长度 29 30 31 unlink(SERV_ADDR); /* 确保bind之前serv.sock文件不存在,bind会创建该文件 */ 32 bind(lfd, (struct sockaddr *)&servaddr, len); /* 参3不能是sizeof(servaddr) */ 33 34 listen(lfd, 20); 35 36 printf("Accept ...\n"); 37 while (1) { 38 len = sizeof(cliaddr); 39 cfd = accept(lfd, (struct sockaddr *)&cliaddr, (socklen_t *)&len); 40 41 len -= offsetof(struct sockaddr_un, sun_path); /* 得到文件名的长度 */ 42 cliaddr.sun_path[len] = '\0'; /* 确保打印时,没有乱码出现 */ 43 44 printf("client bind filename %s\n", cliaddr.sun_path); 45 46 while ((size = read(cfd, buf, sizeof(buf))) > 0) { 47 for (i = 0; i < size; i++) 48 buf[i] = toupper(buf[i]); 49 write(cfd, buf, size); 50 } 51 close(cfd); 52 } 53 close(lfd); 54 55 return 0; 56 }
客户端
#include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/socket.h> #include <strings.h> #include <string.h> #include <ctype.h> #include <arpa/inet.h> #include <sys/un.h> #include <stddef.h> #include "wrap.h" #define SERV_ADDR "serv.socket" #define CLIE_ADDR "clie.socket" int main(void) { int cfd, len; struct sockaddr_un servaddr, cliaddr; char buf[4096]; cfd = socket(AF_UNIX, SOCK_STREAM, 0); bzero(&cliaddr, sizeof(cliaddr)); cliaddr.sun_family = AF_UNIX; strcpy(cliaddr.sun_path,CLIE_ADDR); len = offsetof(struct sockaddr_un, sun_path) + strlen(cliaddr.sun_path); /*计算客户端地址结构有效长度 */ unlink(CLIE_ADDR); bind(cfd, (struct sockaddr *)&cliaddr, len); /* 客户端也需要bind, 不能依赖自动绑定*/ bzero(&servaddr, sizeof(servaddr)); /* 构造server 地址 */ servaddr.sun_family = AF_UNIX; strcpy(servaddr.sun_path,SERV_ADDR); len = offsetof(struct sockaddr_un, sun_path) + strlen(servaddr.sun_path); /* 计算服务器端地址结构有效长度 */ connect(cfd, (struct sockaddr *)&servaddr, len); while (fgets(buf, sizeof(buf), stdin) != NULL) { write(cfd, buf, strlen(buf)); len = read(cfd, buf, sizeof(buf)); write(STDOUT_FILENO, buf, len); } close(cfd); return 0; }