9.11. Socket 物件

傳輸層的 Python 介面是 socket.socket 類別。一個 socket 代表一次網路對話的其中一個端點——一個位址、一個連接埠,以及該對話所使用的協定(UDP 或 TCP)。硬體控制章節開啟了 UART 實例以在線路上通訊;本節則開啟 socket 實例以在網路上通訊。形式是相同的;只是底層的服務能力強大得多。

9.11.1. 建立 socket

有三個引數用來描述一個 socket:它說哪一種 位址族(address family)、它提供哪一種 socket 類型,以及它使用哪一種 協定。預設值涵蓋了本節其餘部分所使用的情況::

import socket

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)   # IPv4 TCP
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)    # IPv4 UDP

應用在幾乎每種情況下都會在兩個常數之間做選擇:

  • AF_INET -- IPv4 位址(一個數字 IP 加上一個連接埠)。最常見的選擇。AF_INET6 是 IPv6 的對應物。

  • SOCK_STREAM -- 一個 TCP 連線,亦即可靠的位元組串流。SOCK_DGRAM 是 UDP 的對應物。

第三個引數(協定)保持為零,這會根據前兩者挑選正確的預設值。完整的建構函式記載於 socket --- socket 模組

9.11.2. socket 上的位址

socket 位址是一個 (host, port) 元組::

("192.168.1.50", 80)
("0.0.0.0", 8000)

host 是字串形式的 IP 位址。port 則是 連接埠 中所涵蓋的 16 位元整數。

有少數幾個 特殊 的 host 字串值得了解:

  • "0.0.0.0" 表示「這台裝置上的每一個 IPv4 介面」。繫結到此位址的伺服器會接受傳往相機所持有的任何位址的連線。

  • "127.0.0.1"localhost——傳往它的流量永遠不會離開該裝置。在測試時很有用。

  • "255.255.255.255" 是本地廣播位址。傳往它的 UDP 資料報會送往本地網段上的每一台裝置。

"example.com" 這樣的網域名稱在 socket 位址中 並非 有效的 host 字串。它們必須先被解析為 IP;名稱與 DNS 涵蓋了執行此操作的 getaddrinfo() 呼叫。

9.11.3. 兩種角色

socket 的生命週期取決於它位於對話的哪一側。一個 用戶端 socket 會呼叫 connect()(對 UDP 而言,則只是 sendto())以與已知的伺服器通訊。一個 伺服器 socket 會呼叫 bind() 以宣告佔用一個連接埠,接著呼叫 listen()accept()(對 TCP 而言)或 recvfrom()(對 UDP 而言)以接收進來的流量。

兩種情況都使用相同的 socket 建構函式;只有之後所呼叫的方法不同。接下來的三個頁面逐一介紹實際的模式:

9.11.4. 關閉 socket

每個 socket 都持有一小部分作業系統狀態(一個連接埠保留、緩衝區、連線的 TCP 狀態)。當應用使用完畢時,close() 會釋放該狀態。一個被遺忘的 socket 是一個會逐漸累積的緩慢洩漏;在一個會開啟連線的迴圈中,漏掉一個 close 最終會耗盡相機可用 socket 的資源池。

最乾淨的模式是 with 陳述式::

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect(addr)
    s.send(b"GET / HTTP/1.0\r\n\r\n")
    ...
# socket is closed automatically here, even on error

Socket 實作了 Python 概覽中所涵蓋的情境管理器協定,因此 with 區塊保證無論該區塊是正常結束還是因引發例外而結束,close() 都會被呼叫。

9.11.5. socket 參考

本頁與接下來的頁面以敘述的形式逐一介紹該 API。關於每個方法、每個旗標以及該模組所公開的每個常數的完整引數層級參考,請參閱 socket --- socket 模組。該參考也是查閱本節未涵蓋的較不常見操作(socket 選項、多播群組成員資格、IPv6 範圍 ID)的地方。