最近在写 MIT 6.824 的 lab,其中读作业框架代码的时候发现当中 RPC 使用的是 Unix Socket,然后另一个被注释的实现是 TCP socket。就联想起 IPC 中的 Socket 方式。那么这三者究竟是怎样的关系呢?

[TOC]

Socket 通信

之前写过很原始的 TCP/UDP 通信,记得当时的代码大概是这个样子的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//  create a socket
if( (listenfd = socket(AF_INET, SOCK_STREAM, 0)) == -1 ){
printf("create socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}

// bind socket to a listen addr
if( bind(listenfd, (struct sockaddr*)&servaddr, sizeof(servaddr)) == -1){
printf("bind socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}

// begin to listen
if( listen(listenfd, 10) == -1){
printf("listen socket error: %s(errno: %d)\n",strerror(errno),errno);
exit(0);
}

// accept a connection and handle
while(1){
if( (connfd = accept(listenfd, (struct sockaddr*)NULL, NULL)) == -1){
printf("accept socket error: %s(errno: %d)",strerror(errno),errno);
continue;
}
  1. 首先创建一个 Socket, 其中 SOCK_STREAM 制定 socket 是一个 TCP socket。
  2. 绑定一个地址
  3. 开始监听地址
  4. accept 一个请求并处理

这是一个比较清晰的样例,其调用的是 linux 关于 socket 的系统调用。

Unix Socket

在做 MIT 6.824 时发现本地 RPC 走的是 Unix Sokcet,而不是原本我印象中的用于网络通信的 Socket。其代码大致如下

1
2
3
4
l, e := net.Listen("unix", sockname)
if e != nil {
log.Fatal("listen error:", e)
}

其通过一个 Socket 文件进行通信,会在 文件系统对应位置生成一个 .sock 文件。是不是很熟悉? 被 docker 折磨过的大概都记得 /var/run/docker.sock 文件吧,docker client 和 docker server 之间的通信方式就是通过 unix socket 协议。Unix socket 是一种很强大的 IPC 通信方式。

其系统调用过程如下:

1
2
3
4
 if ((listenfd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {  
perror("socket error");
exit(1);
}

和 TCP 的过程几乎一摸一样,区别在于 AF_UNIX。
那么 socket 和 unix socket 究竟有什么关系呢,或者说两者是不是一种东西?

tcp socket 和 unix socket 的比较

博客的解释:Unix domain socket 又叫 IPC(inter-process communication 进程间通信) socket,用于实现同一主机上的进程间通信。socket 原本是为网络通讯设计的,但后来在 socket 的框架上发展出一种 IPC 机制,就是 UNIX domain socket。虽然网络 socket 也可用于同一台主机的进程间通讯(通过 loopback 地址 127.0.0.1),但是 UNIX domain socket 用于 IPC 更有效率:不需要经过网络协议栈,不需要打包拆包、计算校验和、维护序号和应答等,只是将应用层数据从一个进程拷贝到另一个进程。这是因为,IPC 机制本质上是可靠的通讯,而网络协议是为不可靠的通讯设计的。

知乎专栏则将其统一为一体:根据通信域的不同可以划分成2种:Unix domain socket 和 Internet domain socket。

stack overflow: 则从权限控制层次作为分析,Unix domain socket 没有经历网络,其更像是文件句柄,依赖文件系统的权限控制;而 TCP/UDP socket 则是网络层的概念,其经历网络的一系列操作后从报文层次(地址、端口等等)进行过滤

1
2
3
4
5
6
7
8
9
raw answer:

A UNIX socket, AKA Unix Domain Socket, is an inter-process communication mechanism that allows bidirectional data exchange between processes running on the same machine.

IP sockets (especially TCP/IP sockets) are a mechanism allowing communication between processes over the network. In some cases, you can use TCP/IP sockets to talk with processes running on the same computer (by using the loopback interface).

UNIX domain sockets know that they’re executing on the same system, so they can avoid some checks and operations (like routing); which makes them faster and lighter than IP sockets. So if you plan to communicate with processes on the same host, this is a better option than IP sockets.

Edit: As per Nils Toedtmann's comment: UNIX domain sockets are subject to file system permissions, while TCP sockets can be controlled only on the packet filter level.

总结

总结一下:

  1. 我更倾向于知乎的一个回答,将 TCP socket 和 Unix scoket 结合在一起称作 socket,因为其系统调用 type 的区别而不同。即 socket 分为两种:Internet domain socket 和 Unix domain socket
  2. unix domain socket 层次更低一些,不涉及具体的网络操作,更像是文件句柄,受文件系统权限控制,也不包含网络解包等操作,所以在本地性能会更好一些。
  3. unix domain socket 不能进行跨主机通信,只能算是 IPC。