socket --- socket 模組

本模組提供對 BSD socket 介面的存取。

與 CPython 的差異

為了效率與一致性,MicroPython 中的 socket 物件直接實作了 stream(類檔案)介面。在 CPython 中,你需要使用 makefile() 方法將 socket 轉換為類檔案物件。MicroPython 仍支援此方法(但為空操作),因此在需要與 CPython 相容時,請務必使用它。

Socket 位址格式

socket 模組的原生 socket 位址格式是一種由 getaddrinfo() 函式回傳的不透明資料類型,必須使用該函式來解析文字位址(包括數字位址):

sockaddr = socket.getaddrinfo('www.micropython.org', 80)[0][-1]
# You must use getaddrinfo() even for numeric addresses
sockaddr = socket.getaddrinfo('127.0.0.1', 80)[0][-1]
# Now you can use that address
sock.connect(sockaddr)

使用 getaddrinfo() 是處理位址最有效率(無論在記憶體或處理能力方面)且最具可攜性的方式。

socket 模組也提供了一種與 CPython 相容、使用元組 (tuple) 指定位址的方式,如下所述。在 OpenMV Cam 上 socket 模組為內建;數字位址可以直接以元組格式給定,但網域名稱必須先用 getaddrinfo() 解析。

總結:

  • 請一律使用 getaddrinfo() 來解析主機名稱。

  • 下述的元組位址可作為數字位址的捷徑,適用於快速試作與互動式使用。

socket 模組的元組位址格式:

  • IPv4:(ipv4_address, port),其中 ipv4_address 是以點記法表示的數字 IPv4 位址字串,例如 "8.8.8.8",而 port 是介於 1-65535 範圍內的整數連接埠號。ipv4_address 不接受網域名稱;請先使用 getaddrinfo() 解析它們。

  • IPv6:(ipv6_address, port, flowinfo, scopeid),其中 ipv6_address 是以冒號記法表示的數字 IPv6 位址字串,例如 "2001:db8::1",而 port 是介於 1-65535 範圍內的整數連接埠號。flowinfo 必須為 0。scopeid 是連結本地 (link-local) 位址的介面範圍識別碼。ipv6_address 不接受網域名稱;請先使用 getaddrinfo() 解析它們。

函式

socket.getaddrinfo(host: str, port: int, af: int = 0, type: int = 0, proto: int = 0, flags: int = 0, /) List[Tuple]

將 host/port 引數轉換為一連串 5 元組,其中包含建立連接到該服務的 socket 所需的所有引數。引數 aftypeproto(其意義與 socket 函式相同)可用於篩選回傳哪一種位址。若某參數未指定或為零,則可能回傳所有位址組合(需由使用者端自行篩選)。

產生的 5 元組清單具有下列結構:

(family, type, proto, canonname, sockaddr)

下列範例示範如何連接到給定的 url:

s = socket.socket()
# This assumes that if "type" is not specified, an address for
# SOCK_STREAM will be returned, which may be not true
s.connect(socket.getaddrinfo('www.micropython.org', 80)[0][-1])

建議的篩選參數用法:

s = socket.socket()
# Guaranteed to return an address which can be connect'ed to for
# stream operation.
s.connect(socket.getaddrinfo('www.micropython.org', 80, 0, SOCK_STREAM)[0][-1])

與 CPython 的差異

在此函式發生錯誤時,CPython 會引發 socket.gaierror 例外(OSError 的子類別)。MicroPython 沒有 socket.gaierror,而是直接引發 OSError。請注意 getaddrinfo() 的錯誤編號自成一個命名空間,可能與 errno 模組的錯誤編號不一致。為了區分 getaddrinfo() 的錯誤,它們以負數表示,而標準系統錯誤則為正數(可透過例外物件的 e.args[0] 屬性取得錯誤編號)。使用負值是暫時性的細節,未來可能變更。

socket.inet_ntop(af: int, bin_addr: bytes) str

將給定位址族系 af 的二進位網路位址 bin_addr 轉換為文字表示:

>>> socket.inet_ntop(socket.AF_INET, b"\x7f\0\0\1")
'127.0.0.1'
socket.inet_pton(af: int, txt_addr: str) bytes

將給定位址族系 af 的文字網路位址 txt_addr 轉換為二進位表示:

>>> socket.inet_pton(socket.AF_INET, "1.2.3.4")
b'\x01\x02\x03\x04'

常數

socket.AF_INET: int

IPv4 位址族系。

socket.AF_INET6: int

IPv6 位址族系。

socket.SOCK_STREAM: int

串流 (TCP) socket 類型。

socket.SOCK_DGRAM: int

資料報 (UDP) socket 類型。

socket.SOCK_RAW: int

原始 (raw) socket 類型。

socket.IPPROTO_IP: int

IP 通訊協定層級。作為 level 引數,與 IP_* 選項一起傳給 setsockopt()

socket.IPPROTO_TCP: int

TCP 通訊協定。你不需要將它傳給 socketSOCK_STREAM socket 類型會自動選用它);它唯一的實際用途是作為 level 引數,與 TCP_* 選項一起傳給 setsockopt()

socket.SOL_SOCKET: int

Socket 選項層級。作為 level 引數,與 SO_* 選項一起傳給 setsockopt()

socket.SO_REUSEADDR: int

允許 socket 繫結到仍處於 TIME_WAIT 狀態的位址/連接埠。

socket.SO_BROADCAST: int

允許將資料報傳送到廣播位址。

socket.SO_KEEPALIVE: int

在已連線的 socket 上啟用週期性傳送 keep-alive 探測封包。

socket.SO_SNDTIMEO: int

傳送逾時(以毫秒為單位),作為 value 引數傳給 setsockopt()

socket.SO_RCVTIMEO: int

接收逾時(以毫秒為單位),作為 value 引數傳給 setsockopt()

socket.IP_ADD_MEMBERSHIP: int

加入一個多播群組。一個 IPPROTO_IP 層級的 setsockopt() 選項。

socket.IP_DROP_MEMBERSHIP: int

離開一個多播群組。一個 IPPROTO_IP 層級的 setsockopt() 選項。

socket.TCP_NODELAY: int

停用 Nagle 演算法。一個 IPPROTO_TCP 層級的 setsockopt() 選項。

socket.MSG_PEEK: int

用於 recv() / recvfrom():回傳資料但不將其從輸入佇列移除。

socket.MSG_DONTWAIT: int

用於 recv() / recvfrom():以非阻塞模式執行操作。

類別

class socket.socket(af: int = AF_INET, type: int = SOCK_STREAM, proto: int = IPPROTO_TCP, /)

使用給定的位址族系、socket 類型與通訊協定編號建立一個新的 socket。多數情況下不需要指定 proto(也不建議指定);type 引數會自動選用所需的通訊協定:

# Create STREAM TCP socket
socket(AF_INET, SOCK_STREAM)
# Create DGRAM UDP socket
socket(AF_INET, SOCK_DGRAM)
close() None

將 socket 標記為已關閉並釋放所有資源。一旦如此,socket 物件上後續的所有操作都會失敗。若通訊協定支援,遠端會收到 EOF 指示。

Socket 在被記憶體回收時會自動關閉,但建議在你完成使用後盡快明確地 close() 它們。

bind(address: Any) None

將 socket 繫結到 address。該 socket 不得已被繫結。

listen(backlog: int = 2) None

讓伺服器能夠接受連線。若指定了 backlog,它必須至少為 0(若更低,則會設為 0);它指定系統在拒絕新連線之前所允許的未接受連線數量。若未指定,則會選用一個合理的預設值。

accept() Tuple['socket', Tuple]

接受一個連線。該 socket 必須已繫結到某位址並正在監聽連線。回傳值是一對 (conn, address),其中 conn 是一個新的 socket 物件,可用於在該連線上傳送與接收資料,而 address 是連線另一端繫結到 socket 的位址。

connect(address: Any) None

連接到位於 address 的遠端 socket。

send(bytes: bytes) int

將資料傳送到 socket。該 socket 必須已連接到遠端 socket。回傳已傳送的位元組數,可能小於資料長度(「短寫入」)。

sendall(bytes: bytes) None

將所有資料傳送到 socket。該 socket 必須已連接到遠端 socket。與 send() 不同,此方法會嘗試傳送所有資料,方法是將資料分塊連續傳送。

此方法在非阻塞 socket 上的行為未定義。因此在 MicroPython 上建議改用 write() 方法,它對阻塞 socket 採用相同的「無短寫入」策略,並在非阻塞 socket 上回傳已傳送的位元組數。

recv(bufsize: int, flags: int = 0) bytes

從 socket 接收資料。回傳值是一個代表所接收資料的 bytes 物件。一次最多接收的資料量由 bufsize 指定。

選用的 flags 引數是訊息旗標 (MSG_PEEKMSG_DONTWAIT) 的位元 OR,其意義與 CPython 中相同。

sendto(bytes: bytes, address: Any) int

將資料傳送到 socket。此 socket 不應連接到遠端 socket,因為目的地 socket 是由 address 指定。

recvfrom(bufsize: int, flags: int = 0) Tuple[bytes, Tuple]

從 socket 接收資料。回傳值是一對 (bytes, address),其中 bytes 是代表所接收資料的 bytes 物件,而 address 是傳送該資料的 socket 的位址。

關於選用的 flags 引數的說明,請參閱 recv() 函式。

setsockopt(level: int, optname: int, value: int | bytes) None

設定給定 socket 選項的值。所需的符號常數定義於 socket 模組中(SO_* 等)。value 可以是整數,或是代表緩衝區的類位元組物件。

settimeout(value: float | None) None

在阻塞式 socket 操作上設定逾時。value 引數可以是表示秒數的非負浮點數,或 None。若給定非零值,則若逾時期限在操作完成前已過去,後續的 socket 操作會引發 OSError 例外。若給定零,socket 會被置於非阻塞模式。若給定 None,socket 會被置於阻塞模式。

一個可攜且通用的替代方式是使用 select.poll 物件。它允許同時等待多個物件(不僅限於 socket,還包括支援輪詢的通用 stream 物件)。範例:

# Instead of:
s.settimeout(1.0)  # time in seconds
s.read(10)  # may timeout

# Use:
poller = select.poll()
poller.register(s, select.POLLIN)
res = poller.poll(1000)  # time in milliseconds
if not res:
    # s is still not ready for input, i.e. operation timed out

與 CPython 的差異

在逾時的情況下,CPython 會引發 socket.timeout 例外,它是 OSError 的子類別。MicroPython 則直接引發 OSError。若你使用 except OSError: 來捕捉例外,你的程式碼在 MicroPython 與 CPython 上都能運作。

setblocking(flag: bool) None

設定 socket 的阻塞或非阻塞模式:若 flag 為 false,socket 會被設為非阻塞,否則設為阻塞模式。

此方法是某些 settimeout() 呼叫的簡寫:

  • sock.setblocking(True) 等同於 sock.settimeout(None)

  • sock.setblocking(False) 等同於 sock.settimeout(0)

makefile(mode: str = 'rb', buffering: int = 0, /) Any

回傳一個與 socket 關聯的檔案物件。確切的回傳類型取決於傳給 makefile() 的引數。其支援僅限於二進位模式('rb'、'wb' 與 'rwb')。不支援 CPython 的引數:encodingerrorsnewline

與 CPython 的差異

由於 MicroPython 不支援緩衝串流,buffering 參數的值會被忽略,並視為 0(無緩衝)。

與 CPython 的差異

關閉由 makefile() 回傳的檔案物件「也會」一併關閉原本的 socket。

read(size: int | None = None) bytes

從 socket 讀取至多 size 個位元組。回傳一個 bytes 物件。若未給定 size,則它會從 socket 讀取所有可用資料直到 EOF;因此此方法在 socket 關閉前不會回傳。此函式會嘗試讀取所要求的資料量(無「短讀取」)。不過在非阻塞 socket 上這可能無法做到,此時會回傳較少的資料。

readinto(buf: bytearray | memoryview, nbytes: int | None = None) int

將位元組讀入 buf。若指定了 nbytes,則最多讀取該數量的位元組。否則,最多讀取 len(buf) 個位元組。與 read() 一樣,此方法遵循「無短讀取」策略。

回傳值:讀取並存入 buf 的位元組數。

readline() bytes

讀取一行,以換行字元結尾。

回傳值:讀取到的該行。

write(buf: bytes) int

將位元組緩衝區寫入 socket。此函式會嘗試將所有資料寫入 socket(無「短寫入」)。不過在非阻塞 socket 上這可能無法做到,此時回傳值會小於 buf 的長度。

回傳值:已寫入的位元組數。

備註

MicroPython 未實作 socket.error。CPython 有一個已棄用的 socket.error 例外,它是 OSError 的別名;在 MicroPython 中,請直接使用 OSError 來捕捉與 socket 相關的錯誤。