介紹
套接字是在伺服器上運行的程序之間,或在不同伺服器上運行的程序之間啟用進程間通信的一種方式。伺服器之間的通信依賴於網絡套接字,它使用互聯網協議(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
命令,在IPv4和IPv6接口上创建两个监听连接的基于TCP的套接字,端口为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 的流套接字
要在現代 Linux 系統上使用ss
命令檢查 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 套接字正在監聽所有可用的 IPv4 接口上的端口8080
。僅在特定 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 套接字正在監聽所有可用的 IPv6 接口上的端口8080
(由::
字符表示,這些字符是 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只连接到套接字,而不发送任何数据。 - 由于您的系统将具有其自己的唯一IP地址,因此使用本地环回
127.0.0.1
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地址上端口8080
上的TCP套接字。您可以忽略第二行,它来自在您终端后台运行的socat进程。
现在,您可以重复相同的连接测试,但使用IPv6。运行以下netcat命令:
您应该收到以下输出:
OutputConnection to ::1 8080 port [tcp/http] succeeded!
突出显示的行是netcat的输出。它表示netcat连接到侦听在环回::1
IPv6地址上端口8080
上的TCP套接字。同样,您可以忽略输出的第二行。
清理套接字,您需要为每个创建的 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
來模擬一個NTP服務器,該服務器在UDP端口123
上聆聽請求。然後,您將使用ss
和nc
命令檢查您創建的套接字。
首先,運行以下socat
命令來創建兩個UDP套接字,它們在IPv4和IPv6接口上聆聽端口123的連接:
您將收到如下類似的輸出,顯示在您的shell會話背景中運行的兩個socat
進程ID。您的進程ID將不同於這裡突出顯示的進程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
標誌來限制輸出:
u
標誌限制輸出為 UDP 套接字。其他標誌與上一個 TCP 示例中使用的標誌相同。
首先運行 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套接字不同,后者需要交换数据包以确认套接字是否可用。
注意:如果此示例中的套接字不可用,则远程系统将返回一个ICMP类型3消息(目标不可达),其中代码为3,表示远程主机上的端口不可达。
基于缺乏错误响应推断套接字可用,假设没有阻止ICMP流量的防火墙或连接问题。如果不通过UDP套接字发送、接收和验证应用程序数据,就无法保证远程UDP端口是打开的并接受数据包。
现在您可以重复相同的连接测试,但使用IPv6。运行以下netcat命令:
您应该收到如下输出:
OutputConnection to ::1 123 port [udp/ntp] succeeded!!
輸出顯示netcat未從監聽回環IPv6地址::1
上端口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
. . .
注意/tmp/stream/sock
行中突出显示的u_str
部分。此字段表示套接字类型为基于流的 UDS。第二行显示类型为u_dgr
,这意味着套接字类型是基于数据报的。
由于 Unix 域套接字是文件,因此通常可以使用常规的 Linux 用户和组权限以及访问控制来限制谁可以连接到套接字。您还可以使用文件系统工具如ls
、mv
、chown
和chmod
来检查和操作 UDS 文件。像 SELinux 这样的工具也可以用于为 UDS 文件标记不同的安全上下文。
要检查文件是否为 UDS 套接字,请使用ls
、file
或stat
实用程序。然而,重要的是要注意,这些工具都无法确定 UDS 是流式还是数据报式的。使用ss
工具获取有关 Unix 域套接字的最完整信息。
要检查文件系统上的套接字,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 Domain Socket。-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