使用Unix套接字进行进程间通信
1. 概述
在本文中,我们将确定什么是 Unix 套接字,我们如何与它们交互,以及我们如何在实际用例示例中连接到它们。
2. 套接字介绍
首先,我们需要对 Linux 世界中的套接字有一个基本的了解。我们将在这里更多地关注 Unix 套接字,但网络套接字的底层 API 非常相似。
2.1. Linux 中的套接字
Linux 中的套接字是双向通信管道。与标准 FIFO 或管道不同,套接字的工作是使用套接字接口而不是文件接口完成的。
让我们使用两个快速命令来了解更多信息。第一个命令是*nc ,它是netcat的缩写。netcat实用程序可用于在 Linux 中涉及网络的许多任务。例如,让我们**使用netcat*快速创建一个 Unix Socket**:
$ nc -U /tmp/demo.sock -l
-U参数告诉netcat使用我们指定的Unix 套接字文件。-l参数告诉它充当服务器端并在指定的套接字上侦听传入连接。
现在,让我们使用*lsof 命令检查netcat*创建的文件。lsof命令是一个实用程序,用于列出和提供有关进程正在使用的文件的信息。-U选项告诉lsof只列出 Unix 套接字文件,但这会列出许多套接字,在我们的例子中,我们希望通过直接将它指定为参数来关注一个特别的套接字:
$ lsof /tmp/demo.sock
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 3491531 admin 3u unix 0x0000000000000000 0t0 25487430 /tmp/demo.sock type=STREAM
我们在这里有一些关于使用*/tmp/demo.sock 的进程的有用信息,包括command*、PID和user。我们看到一个名为FD的字段。**当 Linux 进程需要执行 I/O 操作时,它们通过读取或写入文件描述符来实现。**常见示例是stdin、stdout和stderr,它们分别映射到文件描述符 0、1 和 2。
2.2. 套接字类型
让我们更深入地了解我们之前的lsof命令type=STREAM输出中的最后一个字符串。网络和 Unix 套接字都有多种套接字类型。它们共享两种主要类型,SOCK_STREAM和SOCK_DGRAM。
使用SOCK_STREAM的网络套接字将使用TCP ,而使用SOCK_DGRAM的网络套接字将使用 UDP。由于 UDP 根据定义是不可靠的,任何需要通过网络套接字进行可靠数据传输的进程都应该使用SOCK_STREAM类型的网络套接字。然而,当谈到 Unix Sockets 时,这两种类型都是可靠的。
Unix 套接字的两种类型之间的区别在于SOCK_DGRAM类型保留消息边界但是是无连接的。相反,SOCK_STREAM类型不保留消息边界,而是面向连接的。
从 Linux 2.6.4 开始,出现了另一种 Unix Socket 类型,称为SOCK_SEQPACKET,它既保留了消息边界又是面向连接的。
2.3. SOCK_STREAM与SOCK_DGRAM连接行为
让我们来演示两种主要类型的 Unix 套接字之间的区别。我们可以使用我们方便的netcat命令来做到这一点:
$ nc -U /tmp/demo.sock -u -l
该命令与前面的netcat命令相同,但添加了一个小写的*-u*。这个额外的参数告诉netcat我们需要一个SOCK_DGRAM类型的 Unix Socket 。让我们用lsof检查一下:
$ lsof /tmp/demo.sock
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
nc 55330 admin 3u unix 0x0000000000000000 0t0 678755 /tmp/demo.sock type=DGRAM
现在,我们有了SOCK_DGRAM Unix Socket。让我们连接到它,看看它的行为。我们可以在另一个终端启动netcat客户端:
$ nc -U /tmp/demo.sock -u
如果我们不添加*-u*参数,我们会得到一个错误,告诉我们协议是服务器套接字的错误类型。
我们可以向终端提供输入,当我们按下回车时,我们可以在另一边看到它。由于套接字是双向的,因此双方都可以发送和接收。
现在,我们将终止服务器端。客户熬夜,我们可以继续打字和“发送”数据。我们没有任何迹象表明服务器端已关闭——那是因为SOCK_DGRAM Unix Socket 是无连接的。
现在,如果我们也停止客户端并删除*-u参数以使用SOCK_STREAM类型的 Unix Socket启动netcat*服务器和客户端,我们可以执行相同的测试,我们会看到当服务器端关闭连接,我们的客户端通知它也已关闭。
2.4. Unix 套接字权限
Unix 套接字使用它们所在目录的权限。因此,进程必须对该目录具有写入和执行权限才能创建套接字文件。对于希望连接到套接字的任何进程也是如此。
3. 实际用例示例:mysql.sock和 SSH 转发
以流行的 MySQL 数据库为例,我们可以了解尽可能使用 Unix Sockets 的优势。当使用 Unix 套接字而不是 MySQL 的网络套接字时,由于不必招致 TCP/IP 的开销,所以性能有可衡量的优势。
如果我们决定使用 Unix Socket,也许是为了性能优势,我们仍然希望能够远程管理我们的服务。我们可以看到我们有一个用于 MySQL 的 Unix 套接字:
$ lsof /var/run/mysqld/mysqld.sock
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
mysqld 3230830 mysql 32u unix 0xffff8a6472b84000 0t0 24740620 /var/run/mysqld/mysqld.sock type=STREAM
在我们的管理员笔记本电脑/台式机上,我们打开一个终端并运行以下ssh命令:
$ ssh -N -L 1234:/var/run/mysqld/mysqld.sock demo
上面的命令假设我们连接到同一个用户——在本例中是管理员——作为我们本地系统上的当前会话。它还假定我们有一个包含以下条目的*.ssh/config文件,其中remote_host_ip*是特定于环境的 IP 地址:
Host demo
Hostname <remote_host_ip>
现在,我们打开我们最喜欢的数据库客户端。对于我们数据库客户端中的连接,基于我们之前创建的 ssh 隧道,我们将提供连接信息:
- 主机:本地主机
- 端口:1234
现在,当我们的数据库管理客户端连接到本地主机上的 1234 端口时,连接将通过 ssh 隧道转发,并通过 Unix Socket 连接到 MySQL 数据库。