介绍
套接字是一种在服务器上运行的程序之间或在不同服务器上运行的程序之间启用进程间通信的方式。服务器之间的通信依赖于网络套接字,它们使用互联网协议(IP)来封装和处理数据的发送和接收。
客户端和服务器上的网络套接字都通过它们的套接字地址来引用。地址是传输协议(如传输控制协议(TCP)或用户数据报协议(UDP))、IP地址和端口号的唯一组合。
在本教程中,您将学习以下用于进程间通信的不同类型的套接字:
- 流套接字,它们使用TCP作为底层传输协议
- 数据报套接字,它们使用UDP作为底层传输协议
- Unix域套接字,它们使用本地文件来发送和接收数据,而不是网络接口和IP数据包。
在本教程的每个部分中,您还将学习如何在Linux系统上枚举各种套接字类型。您将使用各种命令行工具来检查每种类型的套接字。
先决条件
本教程中的示例已在Ubuntu 20.04服务器上验证过。只要您在本地计算机或远程服务器上安装了所需工具的等效版本,就可以使用大多数现代Linux发行版来按照本教程操作。
要开始使用Ubuntu 20.04,您需要一个已经按照我们的Ubuntu 20.04初始服务器设置指南进行配置的服务器。
您还需要安装一些其他软件包来检查系统上的套接字。确保您系统的软件包缓存是最新的,使用apt update
命令:
然后使用以下命令安装所需的软件包:
软件包iproute2
包含了ss
实用程序,我们将使用它来检查套接字。我们将使用netcat-openbsd
软件包来安装netcat。请注意,当在命令行上调用netcat时,netcat被缩写为nc
。最后,我们将使用socat
软件包创建示例套接字。
什么是流套接字?
流套接字是面向连接的,这意味着发送到和从网络套接字接收的数据包由主机操作系统按顺序传递给应用程序进行处理。基于网络的流套接字通常使用传输控制协议(TCP)来封装和传输数据到网络接口。
TCP设计为一种可靠的网络协议,依赖于有状态的连接。使用基于TCP的流套接字发送的数据将被远程系统成功接收(假设没有路由、防火墙或其他连接问题)。TCP数据包可以以任何顺序到达物理网络接口。在数据包到达顺序错误的情况下,网络适配器和主机操作系统将确保它们按正确的顺序重新组装,以供应用程序处理。
A typical use for a TCP-based stream socket would be for a web server like Apache or Nginx handling HTTP requests on port 80
, or HTTPS on port 443
. For HTTP, a socket address would be similar to 203.0.113.1:80
, and for HTTPS it would be something like 203.0.113.1:443
.
创建基于TCP的流套接字
在以下示例中,您将使用socat
(SOcket CAT
的缩写)命令来模拟一个监听HTTP请求的Web服务器,监听端口为8080(备用HTTP端口)。然后,您将使用ss
和nc
命令检查套接字。
首先,运行以下socat
命令来创建两个基于TCP的套接字,它们在IPv4和IPv6接口上监听端口8080
以等待连接:
TCP4-LISTEN:8080
和TCP6-LISTEN:8080
参数是要使用的协议类型和端口号。它们告诉socat
在所有 IPv4 和 IPv6 接口上创建 TCP 套接字,并在每个套接字上监听传入连接。socat
可以监听系统上的任何可用端口,因此端口范围从0
到65535
都是套接字选项的有效参数。- 选项
fork
用于确保在处理连接后socat
会继续运行,否则它将自动退出。 - 路径
/dev/null
用于代替远程套接字地址。在这种情况下,它告诉socat
将任何传入输入打印到/dev/null
文件中,以静默丢弃它。 - 标志
ipv6only=1
用于 IPv6 套接字,告诉操作系统该套接字未配置为发送数据包到 IPv4 映射的 IPv6 地址。没有此标志,socat
将绑定到 IPv4 和 IPv6 地址。 - 字符
&
指示 shell 在后台运行命令。此标志将确保在调用其他命令以检查套接字时socat
继续运行。
您将收到以下输出,指示在您的 shell 会话后台运行的两个 socat
进程 ID。您的进程 ID 将与此处突出显示的不同:
Output[1] 434223
[2] 434224
现在您有两个在后台监听TCP端口8080
的socat
进程,您可以使用ss
和nc
实用程序检查套接字。
检查基于TCP的流套接字
要使用ss
命令检查现代Linux系统上的TCP套接字,请使用以下标志运行它以限制输出:
-4
和-6
标志告诉ss
仅检查IPv4或IPv6套接字。省略此选项将显示两组套接字。t
标志限制输出为TCP套接字。默认情况下,ss
工具将显示Linux系统上正在使用的所有类型的套接字。l
标志将输出限制为监听套接字。如果没有此标志,将显示所有TCP连接,其中将包括诸如SSH、连接到Web服务器的客户端或您的系统可能与其他服务器建立的连接。n
标志确保显示端口号而不是服务名称。
首先运行ss -4 -tln
命令,检查您的系统上正在等待连接的IPv4基于TCP的套接字:
您将收到如下输出:
OutputState Recv-Q Send-Q Local Address:Port Peer Address:Port Process
. . .
LISTEN 0 1 0.0.0.0:8080 0.0.0.0:*
. . .
您的输出中可能会有其他行与其他端口相关,这取决于系统上运行的服务。输出中突出显示的0.0.0.0:8080
部分表示IPv4 TCP套接字正在监听端口8080
上的所有可用IPv4接口。仅监听特定IPv4地址的服务将在突出显示的字段中仅显示该IP,例如203.0.113.1:8080
。
现在再次运行相同的ss
命令,但使用-6
标志:
您将收到如下输出:
OutputState Recv-Q Send-Q Local Address:Port Peer Address:Port Process
. . .
LISTEN 0 5 [::]:8080 [::]:*
. . .
您的输出中可能会有其他行与其他端口相关,这取决于系统上运行的服务。输出中突出显示的[::]:8080
部分表示IPv6 TCP套接字正在监听端口8080
上的所有可用IPv6接口(由::
字符指示,这是IPv6表示法中全零地址的写法)。仅监听特定IPv6地址的服务将在突出显示的字段中仅显示该IP,例如[2604:a880:400:d1::3d3:6001]:8080
。
连接到基于TCP的流套接字
到目前为止,您已经学会了如何在IPv4和IPv6接口上创建和枚举TCP套接字。现在您有两个正在监听连接的套接字,您可以使用netcat实用程序尝试连接套接字。
使用netcat测试本地和远程套接字的TCP连接是一种非常有用的故障排除技术,可以帮助隔离系统之间的连接和防火墙问题。
要使用netcat连接到本地回环地址上的IPv4套接字,请运行以下命令:
-
-4
标志告诉netcat使用IPv4。 - 使用
-v
标志将详细输出打印到您的终端。 - –
z
选项确保netcat仅连接到套接字,而不发送任何数据。 - 使用本地回环
127.0.0.1
IP地址,因为您的系统将具有其自己的唯一IP地址。如果您知道系统的IP地址,也可以使用该地址进行测试。例如,如果您的系统的公共或私有IP地址为203.0.113.1,则可以使用该地址替换回环IP。
您将收到以下输出:
OutputConnection to 127.0.0.1 (127.0.0.1) 8080 port [tcp/http-alt] succeeded!
高亮显示的行是netcat的输出。它指示netcat连接到在回环127.0.0.1
IPv4地址上侦听的TCP套接字的端口8080
。您可以忽略第二行,这是在您的终端后台中运行的socat进程的输出。
现在,您可以重复相同的连接测试,但使用IPv6。运行以下netcat命令:
您应该收到以下输出:
OutputConnection to ::1 8080 port [tcp/http] succeeded!
高亮显示的行是netcat的输出。它指示netcat连接到在回环::1
IPv6地址上侦听的TCP套接字的端口8080
。同样,您可以忽略输出的第二行。
要清理您的套接字,您需要为每个创建的 socat 进程运行 fg
(前台)命令。然后,您将使用 CTRL+C
来关闭每个 socat。fg
将进程带到终端的前台,顺序与您运行它们的相反顺序相反,因此当您运行它时,第二个 socat
实例将是您首先与之交互的实例。
运行 fg
将第二个 IPv6 socat 实例带到终端的前台。然后运行 CTRL+C
来关闭它。
您将收到如下输出:
Outputsocat TCP6-LISTEN:8080,ipv6only=1,fork /dev/null
按 CTRL+C
停止该进程。
现在再次运行 fg
来清理第一个 IPv4 套接字。您应该会收到如下输出:
Outputsocat TCP4-LISTEN:8080,fork /dev/null
按 CTRL+C
停止该进程。
您现在已经在系统上创建、检查和连接了 IPv4 和 IPv6 套接字。这些技术和工具可用于本地开发系统或远程生产服务器,因此尝试使用每个工具进行实验,以更熟悉它们如何用于测试和排除故障 TCP 套接字。
什么是数据报套接字?
数据报套接字是无连接的,这意味着从套接字发送和接收的数据包由应用程序单独处理。基于网络的数据报套接字通常使用用户数据报协议 (UDP) 来封装和传输数据。
UDP在数据包头中不编码序列信息,并且协议中没有内置的错误校正。使用数据报套接字的程序必须构建自己的错误处理和数据排序逻辑,以确保成功的数据传输。
UDP套接字通常被域名系统(DNS)服务器使用。默认情况下,DNS服务器使用端口53
来发送和接收域名查询。DNS服务器的示例UDP套接字地址类似于203.0.113.1:53
。
注意:虽然协议未包含在套接字地址的可读版本中,但操作系统通过在地址中包含TCP和UDP协议来区分套接字地址。因此,像203.0.113.1:53
这样的可读套接字地址可能使用任一协议。工具如ss
和较旧的netstat
实用程序用于确定使用了哪种类型的套接字。
网络时间协议(NTP)使用端口123
上的UDP套接字来同步计算机之间的时钟。NTP协议的示例UDP套接字为203.0.113.1:123
。
创建数据报套接字
与之前的TCP套接字示例类似,在本节中,您将再次使用socat
来模拟一个监听UDP端口123
的NTP服务器。然后,您将使用ss
和nc
命令来检查您创建的套接字。
首先,运行以下socat
命令来创建两个UDP套接字,它们在IPv4和IPv6接口上监听端口123:
您将收到类似以下的输出,其中显示了两个socat
进程的ID,这些进程在您的shell会话的后台运行。您的进程ID将与此处突出显示的不同:
Output[1] 465486
[2] 465487
- 每个命令前缀为
sudo
,因为大多数系统上保留了端口0
到1024
。sudo
以管理员权限运行命令,这允许socat
绑定到保留范围内的任何端口。 UDP4-LISTEN:123
和UDP6-LISTEN:123
参数是协议类型和端口。它们告诉socat在IPv4和IPv6接口上的端口123
上创建基于UDP的套接字,并监听传入数据。再次,0-65535范围内的任何端口都是UDP套接字的有效参数。fork
、ipv6only=1
和/dev/null
参数的用法与之前的TCP示例中描述的相同。
现在您已经有两个监听UDP端口123
的socat
进程,您可以使用ss
和nc
工具检查这些套接字。
检查数据报套接字
要在现代Linux系统上使用ss
命令检查UDP套接字,请使用以下-4
、-6
和uln
标志来限制输出:
首先运行ss -4 -uln
命令,检查您系统上正在侦听连接的IPv4 UDP套接字:
您将收到如下输出:
OutputState Recv-Q Send-Q Local Address:Port Peer Address:Port Process
. . .
UNCONN 0 0 0.0.0.0:123 0.0.0.0:*
. . .
根据您系统上运行的服务,您的输出中可能还有其他端口的其他行。输出中突出显示的0.0.0.0:123
部分表示IPv4 UDP套接字在所有IPv4接口上的端口123
上可用。仅在特定IPv4地址上可用的服务将仅在突出显示的字段中显示该IP,例如203.0.113.1:123
。
现在再次运行相同的ss
命令,但加上-6
标志:
您将收到如下输出:
OutputState Recv-Q Send-Q Local Address:Port Peer Address:Port Process
. . .
UNCONN 0 0 [::]:123 [::]:*
. . .
可能会有其他带有其他端口的行出现在您的输出中,这取决于系统上运行哪些服务。输出中突出显示的[::]:123
部分表示IPv6 TCP套接字在所有IPv6接口上的端口123
上可用(如::
字符所示)。仅在特定IPv6地址上可用的服务将仅在突出显示的字段中显示该IP,例如[2604:a880:400:d1::3d3:6001]:123
。
测试数据报套接字
现在您已经熟悉如何在IPv4和IPv6接口上创建和枚举UDP套接字,您可以尝试连接到它们。与TCP套接字一样,您可以使用netcat实用程序尝试UDP套接字。
要连接到上一节教程中创建的端口123
上的示例UDP套接字,请运行以下netcat命令:
- 标志
-4
告诉netcat使用IPv4。 - 选项
-u
指示netcat使用UDP而不是TCP。 - 标志
-v
用于将详细输出打印到您的终端。 - –
z
选项确保netcat仅连接到套接字,而不发送任何数据。 - 本地环回
127.0.0.1
IP地址用于系统将具有自己的唯一IP地址的情况。如果您知道系统的IP地址,也可以使用该地址进行测试。例如,如果您的系统的公共或私有IP地址是203.0.113.1
,您可以将其用于替代环回IP地址。
您将收到以下输出:
OutputConnection to 127.0.0.1 123 port [udp/ntp] succeeded!
输出指示netcat 未从侦听环回127.0.0.1
IPv4地址上端口123
的UDP套接字收到错误。这种缺乏错误响应被用来推断127.0.0.1:123
处的套接字可用。此行为与需要交换数据包以确认套接字是否可用的TCP套接字不同。
注意:如果此示例中的套接字不可用,则远程系统将返回带有代码3的ICMP类型3消息(目标不可达),指示远程主机上的端口不可达。
基于缺乏错误响应推断套接字可用的假设假定没有防火墙或阻止ICMP流量的连接问题。如果不通过UDP套接字发送、接收和验证应用程序数据,则无法保证远程UDP端口是否打开并接受数据包。
现在您可以重复相同的连接测试,但使用IPv6。运行以下netcat命令:
您应该收到以下输出:
OutputConnection to ::1 123 port [udp/ntp] succeeded!!
输出显示,netcat 未从监听回环::1
IPv6地址上端口123
的UDP套接字收到错误。再次,这种缺乏错误响应被用来推断::1:123
处的套接字可用。
要清理您的套接字,您需要对每个创建的socat进程运行fg
(前台)命令。然后,您将使用CTRL+C
来关闭每个socat。
运行fg
将第二个IPv6 socat
实例带到终端的前台。然后运行CTRL+C
来关闭它。
您将收到以下输出:
Outputsudo socat UDP6-LISTEN:123,ipv6only=1,fork /dev/null
按CTRL+C
停止该进程。
现在再次运行fg
以清理第一个IPv4套接字。您将得到以下输出:
Outputsudo socat UDP4-LISTEN:123,fork /dev/null
按CTRL+C
停止该进程。
您现在已经在系统上创建、检查和测试了IPv4和IPv6 UDP套接字。尝试使用每个工具进行实验,以更熟悉它们如何用于测试和排除UDP套接字。
Unix域套接字是什么?
运行在同一服务器上的程序也可以使用Unix域套接字(UDS)进行通信。Unix域套接字可以是基于流的,也可以是基于数据报的。在使用域套接字时,数据直接在操作系统的内核中通过主机文件系统中的文件在程序之间交换。要使用域套接字发送或接收数据,程序读取和写入它们共享的套接字文件,完全绕过基于网络的套接字和协议。
Unix域套接字被广泛用于不需要连接到网络接口的数据库系统。例如,Ubuntu上的MySQL默认使用名为/var/run/mysqld/mysql.sock
的文件与本地客户端进行通信。客户端从套接字读取和写入,MySQL服务器本身也是如此。
PostgreSQL是另一个使用套接字进行本地非网络通信的数据库系统。通常情况下,它默认使用/run/postgresql/.s.PGSQL.5432
作为其套接字文件。
创建Unix域套接字
在前几节中,您探索了TCP如何与流套接字配合使用,以及UDP如何与数据报套接字配合使用。在本节中,您将使用socat
创建基于流和基于数据报的Unix域套接字,而不使用TCP或UDP封装数据以发送到网络。然后,您将使用ss
和nc
命令检查您创建的套接字。最后,您将学习如何使用netcat测试Unix域套接字。
要开始,请运行以下socat
命令以创建两个套接字文件:
- 第一个命令指示socat使用
unix-listen
地址类型创建套接字,这将创建一个基于流的UDS。 - 第二个命令将
unix-recvfrom
指定为套接字类型,这将创建一个基于数据报的UDS。 - 两个命令都在
:
分隔符后指定文件名。文件名是套接字本身的地址。对于第一个流示例,它是/tmp/stream.sock
,对于第二个数据报示例,它是/tmp/datagram.sock
。请注意,套接字的名称是任意的,但在故障排除时最好是描述性的。 fork
和/dev/null
参数的使用方式与流和数据报套接字示例部分中描述的方式相同。
现在您已经创建了两个UDS套接字,您可以使用ss
和nc
工具来检查它们。
检查 Unix 域套接字
要列出所有监听中的 Unix 域套接字,请运行 ss -xln
命令。 x
标志确保只显示域套接字。
您将收到如下输出:
OutputNetid State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
. . .
u_str LISTEN 0 5 /tmp/stream.sock 436470 * 0
u_dgr UNCONN 0 0 /tmp/datagram.sock 433843 * 0
. . .
注意高亮显示的 u_str
部分的 /tmp/stream/sock
行。此字段表示套接字类型为基于流的 UDS。第二行显示类型为 u_dgr
,表示套接字类型为基于数据报的。
由于 Unix 域套接字是文件,因此通常可以使用常规的 Linux 用户和组权限以及访问控制来限制谁可以连接到套接字。您还可以使用文件系统工具如 ls
、mv
、chown
和 chmod
来检查和操作 UDS 文件。诸如 SELinux 等工具也可以用于为 UDS 文件打上不同的安全上下文标签。
要检查文件是否为 UDS 套接字,请使用 ls
、file
或 stat
实用程序。但是,重要的是要注意,这些工具都无法确定 UDS 是流还是数据报型的。对于 Unix 域套接字的最完整信息,请使用 ss
工具。
要检查文件系统上的套接字,stat
实用程序显示最相关的信息。请在您之前创建的套接字上运行它:
你将会收到如下输出:
Output File: /tmp/stream.sock
Size: 0 Blocks: 1 IO Block: 131072 socket
Device: 48h/72d Inode: 1742 Links: 1
Access: (0755/srwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-01 18:10:25.025755168 +0000
Modify: 2021-03-01 18:10:25.025755168 +0000
Change: 2021-03-01 18:22:42.678231700 +0000
Birth: -
File: /tmp/datagram.sock
Size: 0 Blocks: 1 IO Block: 131072 socket
Device: 48h/72d Inode: 1743 Links: 1
Access: (0755/srwxr-xr-x) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2021-03-01 18:10:25.025755168 +0000
Modify: 2021-03-01 18:10:25.025755168 +0000
Change: 2021-03-01 18:10:25.025755168 +0000
Birth: -
注意对于每个文件,类型都是socket
(在输出的最右侧高亮显示),并且访问模式在文件权限之前有一个s
字符。
ls
实用程序还会指示文件是否为套接字。运行ls -l
来检查文件:
你将会收到如下输出。再次注意,对于套接字,文件模式在文件权限字段之前包括s
字符:
Outputsrwxr-xr-x 1 root root 0 Mar 1 18:10 /tmp/datagram.sock
srwxr-xr-x 1 root root 0 Mar 1 18:10 /tmp/stream.sock
现在你已经创建了Unix域套接字,并学会了如何使用ss
和各种基于文件系统的工具来检查它们,下一步是使用像netcat这样的工具测试套接字。
测试Unix域套接字
netcat实用程序可用于连接Unix域套接字,以及你在本教程前面已经学到的TCP和UDP套接字。要连接到你创建的示例套接字,你需要在运行netcat命令时指定额外的-U
标志。此标志告诉netcat连接到一个UDS,而不是基于TCP或UDP的网络套接字。
此外,如果套接字是基于数据报的,你将使用-u
标志指示netcat使用数据报,就像我们在本教程的数据报套接字部分学到的那样。
让我们开始通过以下命令连接基于流的UDS套接字:
-U
告诉netcat正在连接到Unix域套接字。 –z
选项确保netcat只连接到套接字,而不发送任何数据。 /tmp/stream.sock
是文件系统上套接字的地址。
运行该命令时,您将不会从netcat接收任何输出。但是,如果套接字不可用,netcat将输出如下错误消息:
Outputnc: unix connect failed: No such file or directory
nc: /tmp/stream.sock: No such file or directory
因此,在测试基于流的UDS套接字时,如果netcat没有输出,则表示连接成功。
重复测试过程,这次测试基于数据报的UDS:
添加额外的 -u
标志以告诉netcat远程套接字是数据报套接字。如果测试成功,您将不会收到任何输出。
如果地址处没有套接字,则会收到以下错误:
Outputnc: unix connect failed: No such file or directory
nc: /tmp/datagram.sock: No such file or directory
要清理您的套接字,您需要为您创建的每个socat进程运行 fg
(前台)命令。然后,您将使用 CTRL+C
关闭每个socat。
运行 fg
将基于数据报的 socat
实例移到终端的前台:
您将收到以下输出:
Outputsocat unix-recvfrom:/tmp/datagram.sock,fork /dev/null
运行 CTRL+C
来关闭它。您将不会收到任何输出。
现在再次运行 fg
来清理第一个基于流的UDS套接字。
同样,您应该会收到以下输出:
Outputsocat unix-listen:/tmp/stream.sock,fork /dev/null
运行 CTRL+C
结束进程。您将不会收到任何输出。
您现在已经在您的系统上创建、检查和测试了Unix数据报套接字。尝试使用netcat和socat
进行实验,以更熟悉如何通过UDS发送和接收数据,以及如何测试和排除Unix域套接字。
结论
在本教程中,您探索了在Linux系统上如何使用不同类型的套接字。您了解了基于流的套接字,它们通常使用TCP进行网络通信。您还了解了基于数据报的套接字,它们使用UDP在网络上传输数据。最后,您探索了Unix域套接字如何在本地服务器上可以是流式的或数据报式的。
在每个部分中,您使用了ss
实用程序来收集有关Linux系统上套接字的信息。您了解了ss
工具提供的不同标志如何帮助您在检查系统上的套接字时将其输出限制为特定类型的套接字。
最后,您使用了netcat和socat工具来创建并连接到本教程中讨论的三种不同类型的套接字。netcat实用程序被广泛用于连接套接字,但它也可以创建套接字。其文档(man nc)包含了许多示例,说明了它可以在任一模式下使用的方式。socat实用程序是一个更高级的工具,可以用于连接到许多不在本教程中涵盖的不同类型的套接字。其文档(man socat)中还包含了许多不同用法的示例。
理解套接字是什么以及它们如何工作是核心的系统管理技能。在本教程中尝试过的工具和技术将帮助您更熟悉套接字,并且在您的服务器和应用程序之间无法正确通信时如何进行故障排除。
Source:
https://www.digitalocean.com/community/tutorials/understanding-sockets