当前Linux IPC最常用的方法就是UNIX Domain Socket和TCP Socket了。TCP Socket的基本用法可以看:Linux Socket TCP通信基本用法。这两种IPC一个很大的区别就是:
- TCP使用IP + Port来标记进程;
- UNIX Domain Socket使用文件来标记进程;
由于上面差异,在Socket API标记地址的结构体也有所不同,Domain Socket的结构体是struct sockaddr_un
,里面包含了一个文件路径,这个路径就是用来标记进程的。下面,用一个例子来说明:
服务端
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define SOCK_SERV "/root/server_sock"
int main()
{
int fd, connfd, size, ret;
struct sockaddr_un un;
char buff[20] = {0};
char *msg = "Welcome, client!\n";
size_t msglen = strlen(msg) + 1;
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, SOCK_SERV);
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) goto fail;
size = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
ret = bind(fd, (struct sockaddr *)&un, size) < 0;
if (ret < 0) goto fail;
ret = listen(fd, 10);
if (ret < 0) goto fail;
printf("server listen...\n");
while (1) {
socklen_t len = sizeof(un);
connfd = accept(fd, (struct sockaddr *)&un, &len);
if (connfd < 0) goto fail;
printf("accept fd %d\n", connfd);
recv(connfd, buff, sizeof(buff), 0);
printf("recv %s", buff);
send(connfd, msg, msglen, 0);
printf("send %s", msg);
close(connfd);
}
close(fd);
return 0;
fail:
perror("error");
exit(1);
}
客户端
#include <stdlib.h>
#include <stdio.h>
#include <stddef.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <unistd.h>
#define SOCK_CLI "/root/client_sock"
#define SOCK_SERV "/root/server_sock"
int main()
{
struct sockaddr_un un;
int fd, len, ret;
fd = socket(AF_UNIX, SOCK_STREAM, 0);
if (fd < 0) goto fail;
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, SOCK_CLI);
len = offsetof(struct sockaddr_un, sun_path) + strlen(un.sun_path);
ret = bind(fd, (struct sockaddr *)&un, len);
if (ret < 0) goto fail;
memset(&un, 0, sizeof(un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, SOCK_SERV);
len = offsetof(struct sockaddr_un, sun_path) + strlen(SOCK_SERV);
ret = connect(fd, (struct sockaddr *)&un, len);
if (ret < 0) goto fail;
char *msg = "Hi, server!\n";
char buff[20] = {0};
size_t msglen = strlen(msg) + 1;
send(fd, msg, msglen, 0);
printf("send msg: %s", msg);
recv(fd, buff, sizeof(buff), 0);
printf("recv msg: %s", buff);
close(fd);
return 0;
fail:
perror("error");
exit(1);
}
运行
一个简易的makefile如下,运行时在两个console窗口分别启动即可,注意如果socket文件历史已存在,就先删除掉。
CFLAGS = -Werror -Wall
COMPILE = $(CC) $^ $(CFLAGS) -o $@
.PHONY : all
all : server.bin client.bin
server.bin : server.c
$(COMPILE)
client.bin : client.c
$(COMPILE)
.PHONY : clean
clean :
rm *.bin
就API的使用而言,Domain Socket和TCP Socket几乎差不多,甚至TCP Socket还更加方便一些,也方便以后跨主机扩展。就效率而言,本机TCP Socket通信,Linux做了很多优化,两者也差不了多少。所以个人还是倾向于用TCP进行IPC.