一。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;
}