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 兼容的方式,使用元组来指定地址,如下所述。在 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 是链路本地地址的接口作用域标识符。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

原始 socket 类型。

socket.IPPROTO_IP: int

IP 协议层级。与 IP_* 选项一起作为 level 参数传给 setsockopt()

socket.IPPROTO_TCP: int

TCP 协议。你不需要将它传给 socketSOCK_STREAM socket 类型会自动选定它);它唯一的实际用途是与 TCP_* 选项一起作为 level 参数传给 setsockopt()

socket.SOL_SOCKET: int

socket 选项层级。与 SO_* 选项一起作为 level 参数传给 setsockopt()

socket.SO_REUSEADDR: int

允许 socket 绑定到一个仍处于 TIME_WAIT 状态的地址/端口。

socket.SO_BROADCAST: int

允许向广播地址发送数据报。

socket.SO_KEEPALIVE: int

在已连接的 socket 上启用周期性保活探测的发送。

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,将被设为 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)的按位或,其含义与 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 相关的错误。