Contents

使用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*、PIDuser。我们看到一个名为FD的字段。**当 Linux 进程需要执行 I/O 操作时,它们通过读取或写入文件描述符来实现。**常见示例是stdinstdoutstderr,它们分别映射到文件描述符 0、1 和 2。

2.2. 套接字类型

让我们更深入地了解我们之前的lsof命令type=STREAM输出中的最后一个字符串。网络和 Unix 套接字都有多种套接字类型。它们共享两种主要类型,SOCK_STREAMSOCK_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_STREAMSOCK_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 数据库