18.1. socket — 底层网络接口

源代码: Lib/socket.py


这个模块提供了访问BSD*套接字*的接口。在所有现代Unix系统、Windows、macOS和其他一些平台上可用。

注解

一些行为可能因平台不同而异,因为调用的是操作系统的套接字API。

这个Python接口是用Python的面向对象风格对Unix系统调用和套接字库接口的直译:函数 socket() 返回一个 套接字对象 ,其方法是对各种套接字系统调用的实现。形参类型一般与C接口相比更高级:例如在Python文件 read()write() 操作中,接收操作的缓冲区分配是自动的,发送操作的缓冲区长度是隐式的。

参见

模块 socketserver
用于简化网络服务端编写的类。
模块 ssl
套接字对象的TLS/SSL封装。

18.1.1. 套接字协议族

根据系统以及构建选项,此模块提供了各种 socket 协议簇。

特定的 socket 对象需要的地址格式将根据此 socket 对象被创建时指定的地址族被自动选择。socket 地址表示如下:

  • 一个绑定在文件系统节点上的 AF_UNIX 套接字的地址表示为一个字符串,使用文件系统字符编码和 'surrogateescape' 错误回调方法(see PEP 383)。一个地址在 Linux 的抽象命名空间被返回为带有初始的 null 字节的 bytes-like object ;注意在这个命名空间种的套接字可能与普通文件系统套接字通信,所以打算运行在 Linux 上的程序可能需要解决两种地址类型。当传递为参数时,一个字符串或字节类对象可以用于任一类型的地址。

    在 3.3 版更改: 以前,AF_UNIX 套接字路径被假设使用 UTF-8 编码。

    在 3.5 版更改: 现在支持可写的 字节类对象

  • 一对 (host, port) 被用于 AF_INET 地址族,host 是一个代表互联网域名表示法之内主机名或者一个 IPv4 地址的字符串,例如 'daring.cwi.nl''100.50.200.5'port 是一个整数。

  • 对于 AF_INET6 地址族,使用一个四元组 (host, port, flowinfo, scopeid)flowinfoscopeid 代表了 C 库里 struct sockaddr_in6 中的 sin6_flowinfosin6_scope_id 成员。 对于 socket 模块中的方法, flowinfoscopeid 可以被省略,只为了向后兼容。注意,scopeid 的省略可能会导致 IPv6 地址的操作范围问题。

  • AF_NETLINK 套接字由一对 (pid, groups) 表示。

  • 指定 AF_TIPC 地址族可以使用仅 Linux 支持的 TIPC 协议。TIPC 是一种开放的、非基于 IP 的网络协议,旨在用于集群计算环境。其地址用元组表示,其中的字段取决于地址类型。一般元组形式为 (addr_type, v1, v2, v3 [, scope]),其中:

    • addr_typeTIPC_ADDR_NAMESEQTIPC_ADDR_NAMETIPC_ADDR_ID 中的一个。

    • scopeTIPC_ZONE_SCOPETIPC_CLUSTER_SCOPETIPC_NODE_SCOPE 中的一个。

    • 如果 addr_typeTIPC_ADDR_NAME,那么 v1 是服务器类型,v2 是端口标识符,v3 应为 0。

      如果 addr_typeTIPC_ADDR_NAMESEQ,那么 v1 是服务器类型,v2 是端口号下限,而 v3 是端口号上限。

      如果 addr_typeTIPC_ADDR_ID,那么 v1 是节点 (node),v2 是 ref,v3 应为 0。

  • AF_CAN 地址族使用元组 (interface, ),其中 interface 是表示网络接口名称的字符串,如 'can0'。网络接口名 '' 可以用于接收本族所有网络接口的数据包。

  • PF_SYSTEM 协议簇的 SYSPROTO_CONTROL 协议接受一个字符串或元组 (id, unit)。其中字符串是内核控件的名称,该控件使用动态分配的 ID。而如果 ID 和内核控件的单元 (unit) 编号都已知,或使用了已注册的 ID,可以采用元组。

    3.3 新版功能.

  • AF_BLUETOOTH 支持以下协议和地址格式:

    • BTPROTO_L2CAP 接受 (bdaddr, psm),其中 bdaddr 为字符串格式的蓝牙地址,psm 是一个整数。

    • BTPROTO_RFCOMM 接受 (bdaddr, channel),其中 bdaddr 为字符串格式的蓝牙地址,channel 是一个整数。

    • BTPROTO_HCI 接受 (device_id,),其中 device_id 为整数或字符串,它表示接口对应的蓝牙地址(具体取决于你的系统,NetBSD 和 DragonFlyBSD 需要蓝牙地址字符串,其他系统需要整数)。

      在 3.2 版更改: 添加 NetBSD 和 DragonFlyBSD 支持。

    • BTPROTO_SCO 接受 bdaddr,其中 bdaddrbytes 对象,其中含有字符串格式的蓝牙地址(如 b'12:23:34:45:56:67' ),FreeBSD 不支持此协议。

  • Certain other address families (AF_PACKET, AF_CAN) support specific representations.

For IPv4 addresses, two special forms are accepted instead of a host address: the empty string represents INADDR_ANY, and the string '<broadcast>' represents INADDR_BROADCAST. This behavior is not compatible with IPv6, therefore, you may want to avoid these if you intend to support IPv6 with your Python programs.

如果你在 IPv4/v6 套接字地址的 host 部分中使用了一个主机名,此程序可能会表现不确定行为,因为 Python 使用 DNS 解析返回的第一个地址。套接字地址在实际的 IPv4/v6 中以不同方式解析,根据 DNS 解析和/或 host 配置。为了确定行为,在 host 部分中使用数字地址。

所有的错误都抛出异常。对于无效参数类型和内存溢出异常情况可能抛出普通异常;从 Python 3.3 开始,与套接字或地址语义有关的错误抛出 OSError 或它的子类之一(常用 socket.error)。

可以用 setblocking() 设置非阻塞模式。一个基于超时的 generalization 通过 settimeout() 支持。

18.1.2. 模块内容

socket 模块导出以下元素。

18.1.2.1. 异常

exception socket.error

一个被弃用的 OSError 的别名。

在 3.3 版更改: 根据 PEP 3151,这个类是 OSError 的别名。

exception socket.herror

OSError 的子类,本异常通常表示与地址相关的错误,比如那些在 POSIX C API 中使用了 h_errno 的函数,包括 gethostbyname_ex()gethostbyaddr()。附带的值是一对 (h_errno, string),代表库调用返回的错误。h_errno 是一个数字,而 string 表示 h_errno 的描述,它们由 C 函数 hstrerror() 返回。

在 3.3 版更改: 此类是 OSError 的子类。

exception socket.gaierror

OSError 的子类,本异常来自 getaddrinfo()getnameinfo(),表示与地址相关的错误。附带的值是一对 (error, string),代表库调用返回的错误。string 表示 error 的描述,它由 C 函数 gai_strerror() 返回。数字值 error 与本模块中定义的 EAI_* 常量之一匹配。

在 3.3 版更改: 此类是 OSError 的子类。

exception socket.timeout

OSError 的子类,当套接字发生超时,且事先已调用过 settimeout() (或隐式地通过 setdefaulttimeout() )启用了超时,则会抛出此异常。附带的值是一个字符串,其值总是 “timed out”。

在 3.3 版更改: 此类是 OSError 的子类。

18.1.2.2. 常量

AF_* 和 SOCK_* 常量现在都在 AddressFamilySocketKind 这两个 IntEnum 集合内。

3.4 新版功能.

socket.AF_UNIX
socket.AF_INET
socket.AF_INET6

这些常量表示地址(和协议)簇,用于 socket() 的第一个参数。如果 AF_UNIX 常量未定义,即表示不支持该协议。不同系统可能会有更多其他常量可用。

socket.SOCK_STREAM
socket.SOCK_DGRAM
socket.SOCK_RAW
socket.SOCK_RDM
socket.SOCK_SEQPACKET

这些常量表示套接字类型,用于 socket() 的第二个参数。不同系统可能会有更多其他常量可用。(一般只有 SOCK_STREAMSOCK_DGRAM 可用)

socket.SOCK_CLOEXEC
socket.SOCK_NONBLOCK

这两个常量(如果已定义)可以与上述套接字类型结合使用,并允许你设置一些原子性的 flags (从而避免可能的竞争条件和单独调用的需要)。

Availability: Linux >= 2.6.27.

3.2 新版功能.

SO_*
socket.SOMAXCONN
MSG_*
SOL_*
SCM_*
IPPROTO_*
IPPORT_*
INADDR_*
IP_*
IPV6_*
EAI_*
AI_*
NI_*
TCP_*

此列表内的许多常量,记载在 Unix 文档中的套接字和/或 IP 协议部分,同时也定义在本 socket 模块中。它们通常用于套接字对象的 setsockopt()getsockopt() 方法的参数中。在大多数情况下,仅那些在 Unix 头文件中有定义的符号会在本模块中定义,部分符号提供了默认值。

socket.AF_CAN
socket.PF_CAN
SOL_CAN_*
CAN_*

此列表内的许多常量,记载在 Linux 文档中,同时也定义在本 socket 模块中。

Availability: Linux >= 2.6.25.

3.3 新版功能.

socket.CAN_BCM
CAN_BCM_*

CAN 协议簇内的 CAN_BCM 是广播管理器(Bbroadcast Manager – BCM)协议,广播管理器常量在 Linux 文档中有所记载,在本 socket 模块中也有定义。

Availability: Linux >= 2.6.25.

3.4 新版功能.

socket.CAN_RAW_FD_FRAMES

Enables CAN FD support in a CAN_RAW socket. This is disabled by default. This allows your application to send both CAN and CAN FD frames; however, you one must accept both CAN and CAN FD frames when reading from the socket.

此常量在 Linux 文档中有所记载。

Availability: Linux >= 3.6.

3.5 新版功能.

socket.AF_RDS
socket.PF_RDS
socket.SOL_RDS
RDS_*

此列表内的许多常量,记载在 Linux 文档中,同时也定义在本 socket 模块中。

Availability: Linux >= 2.6.30.

3.3 新版功能.

SIO_*
RCVALL_*

Windows 的 WSAIoctl() 的常量。这些常量用于套接字对象的 ioctl() 方法的参数。

TIPC_*

TIPC 相关常量,与 C socket API 导出的常量一致。更多信息请参阅 TIPC 文档。

Availability: BSD, OSX.

3.4 新版功能.

socket.has_ipv6

本常量为一个布尔值,该值指示当前平台是否支持 IPv6。

socket.BDADDR_ANY
socket.BDADDR_LOCAL

这些是字符串常量,包含蓝牙地址,这些地址具有特殊含义。例如,当用 BTPROTO_RFCOMM 指定绑定套接字时, BDADDR_ANY 表示“任何地址”。

socket.HCI_FILTER
socket.HCI_TIME_STAMP
socket.HCI_DATA_DIR

BTPROTO_HCI 一起使用。 HCI_FILTER 在 NetBSD 或 DragonFlyBSD 上不可用。 HCI_TIME_STAMPHCI_DATA_DIR 在 FreeBSD、NetBSD 或 DragonFlyBSD 上不可用。

18.1.2.3. 函数

18.1.2.3.1. 创建套接字

下列函数都能创建 套接字对象.

socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None)

Create a new socket using the given address family, socket type and protocol number. The address family should be AF_INET (the default), AF_INET6, AF_UNIX, AF_CAN or AF_RDS. The socket type should be SOCK_STREAM (the default), SOCK_DGRAM, SOCK_RAW or perhaps one of the other SOCK_ constants. The protocol number is usually zero and may be omitted or in the case where the address family is AF_CAN the protocol should be one of CAN_RAW or CAN_BCM. If fileno is specified, the other arguments are ignored, causing the socket with the specified file descriptor to return. Unlike socket.fromfd(), fileno will return the same socket and not a duplicate. This may help close a detached socket using socket.close().

新创建的套接字是 不可继承的

在 3.3 版更改: 添加了 AF_CAN 簇。添加了 AF_RDS 簇。

在 3.4 版更改: 添加了 CAN_BCM 协议。

在 3.4 版更改: 返回的套接字现在是不可继承的。

socket.socketpair([family[, type[, proto]]])

构建一对已连接的套接字对象,使用给定的地址簇、套接字类型和协议号。地址簇、套接字类型和协议号与上述 socket() 函数相同。默认地址簇为 AF_UNIX (需要当前平台支持,不支持则默认为 AF_INET )。

新创建的套接字都是 不可继承的

在 3.2 版更改: 现在,返回的套接字对象支持全部套接字 API,而不是全部 API 的一个子集。

在 3.4 版更改: 返回的套接字现在都是不可继承的。

在 3.5 版更改: 添加了 Windows 支持。

socket.create_connection(address[, timeout[, source_address]])

连接到一个 TCP 服务,该服务正在侦听 Internet address (用二元组 (host, port) 表示)。连接后返回套接字对象。这是比 socket.connect() 更高级的函数:如果 host 是非数字主机名,它将尝试从 AF_INETAF_INET6 解析它,然后依次尝试连接到所有可能的地址,直到连接成功。这使得编写兼容 IPv4 和 IPv6 的客户端变得容易。

传入可选参数 timeout 可以在套接字实例上设置超时(在尝试连接前)。如果未提供 timeout,则使用由 getdefaulttimeout() 返回的全局默认超时设置。

如果提供了 source_address,它必须为二元组 (host, port),以便套接字在连接之前绑定为其源地址。如果 host 或 port 分别为 ‘’ 或 0,则使用操作系统默认行为。

在 3.2 版更改: 添加了*source_address* 参数

socket.fromfd(fd, family, type, proto=0)

复制文件描述符 fd (一个由文件对象的 fileno() 方法返回的整数),然后从结果中构建一个套接字对象。地址簇、套接字类型和协议号与上述 socket() 函数相同。文件描述符应指向一个套接字,但不会专门去检查——如果文件描述符是无效的,则对该对象的后续操作可能会失败。本函数很少用到,但是在将套接字作为标准输入或输出传递给程序(如 Unix inet 守护程序启动的服务器)时,可以使用本函数获取或设置套接字选项。套接字须处于阻塞模式。

新创建的套接字是 不可继承的

在 3.4 版更改: 返回的套接字现在是不可继承的。

socket.fromshare(data)

根据 socket.share() 方法获得的数据实例化套接字。套接字须处于阻塞模式。

Availability: Windows.

3.3 新版功能.

socket.SocketType

这是一个 Python 类型对象,表示套接字对象的类型。它等同于 type(socket(...))

18.1.2.3.2. 其他功能

socket 模块还提供各种与网络相关的服务:

socket.getaddrinfo(host, port, family=0, type=0, proto=0, flags=0)

host/port 参数转换为 5 元组的序列,其中包含创建(连接到某服务的)套接字所需的所有参数。host 是域名,是字符串格式的 IPv4/v6 地址或 Noneport 是字符串格式的服务名称,如 'http' 、端口号(数字)或 None。传入 None 作为 hostport 的值,相当于将 NULL 传递给底层 C API。

可以指定 familytypeproto 参数,以缩小返回的地址列表。向这些参数分别传入 0 表示保留全部结果范围。flags 参数可以是 AI_* 常量中的一个或多个,它会影响结果的计算和返回。例如,AI_NUMERICHOST 会禁用域名解析,此时如果 host 是域名,则会抛出错误。

本函数返回的 5 元组列表具有以下结构:

(family, type, proto, canonname, sockaddr)

在这些元组中,familytypeproto 都是整数,可以用于传递给 socket() 函数。如果 flags 参数有一部分是 AI_CANONNAME,那么 canonname 将是表示 host 的规范名称的字符串。否则 canonname 将为空。sockaddr 是一个表示套接字地址的元组,具体格式取决于返回的 family (对于 AF_INET,是一个 (address, port) 二元组,对于 AF_INET6,是一个 (address, port, flow info, scope id) 四元组),可以用于传递给 socket.connect() 方法。

下面的示例获取了 TCP 连接地址信息,假设该连接通过 80 端口连接至 example.org (如果系统未启用 IPv6,则结果可能会不同):

>>> socket.getaddrinfo("example.org", 80, proto=socket.IPPROTO_TCP)
[(<AddressFamily.AF_INET6: 10>, <SocketType.SOCK_STREAM: 1>,
 6, '', ('2606:2800:220:1:248:1893:25c8:1946', 80, 0, 0)),
 (<AddressFamily.AF_INET: 2>, <SocketType.SOCK_STREAM: 1>,
 6, '', ('93.184.216.34', 80))]

在 3.2 版更改: 现在可以使用关键字参数的形式来传递参数。

socket.getfqdn([name])

返回 name 的全限定域名 (Fully Qualified Domain Name – FQDN)。如果 name 省略或为空,则将其解释为本地主机。为了查找全限定名称,首先将检查由 gethostbyaddr() 返回的主机名,然后是主机的别名(如果存在)。选中第一个包含句点的名字。如果无法获取全限定域名,则返回由 gethostname() 返回的主机名。

socket.gethostbyname(hostname)

将主机名转换为 IPv4 地址格式。IPv4 地址以字符串格式返回,如 '100.50.200.5'。如果主机名本身是 IPv4 地址,则原样返回。更完整的接口请参考 gethostbyname_ex()gethostbyname() 不支持 IPv6 域名解析,应使用 getaddrinfo() 来支持 IPv4/v6 双协议栈。

socket.gethostbyname_ex(hostname)

将主机名转换为 IPv4 地址格式的扩展接口。返回三元组 (hostname, aliaslist, ipaddrlist),其中 hostname 是响应给定 ip_address 的主要主机名,aliaslist 是相同地址的其他可用主机名的列表(可能为空),而 ipaddrlist 是 IPv4 地址列表,包含相同主机名、相同接口的不同地址(通常是一个地址,但不总是如此)。gethostbyname_ex() 不支持 IPv6 名称解析,应使用 getaddrinfo() 来支持 IPv4/v6 双协议栈。

socket.gethostname()

返回一个字符串,包含当前正在运行 Python 解释器的机器的主机名。

注意: gethostname() 并不总是返回全限定域名,必要的话请使用 getfqdn()

socket.gethostbyaddr(ip_address)

返回三元组 (hostname, aliaslist, ipaddrlist),其中 hostname 是响应给定 ip_address 的主要主机名,aliaslist 是相同地址的其他可用主机名的列表(可能为空),而 ipaddrlist 是 IPv4/v6 地址列表,包含相同主机名、相同接口的不同地址(很可能仅包含一个地址)。要查询全限定域名,请使用函数 getfqdn()gethostbyaddr() 支持 IPv4 和 IPv6。

socket.getnameinfo(sockaddr, flags)

将套接字地址 sockaddr 转换为二元组 (host, port)host 的形式可能是全限定域名,或是由数字表示的地址,具体取决于 flags 的设置。同样,port 可以包含字符串格式的端口名称或数字格式的端口号。

socket.getprotobyname(protocolname)

将 Internet 协议名称(如 'icmp' )转换为常量,该常量适用于 socket() 函数的第三个(可选)参数。通常只有在 “raw” 模式 (SOCK_RAW) 中打开的套接字才需要使用该常量。对于正常的套接字模式,协议省略或为零时,会自动选择正确的协议。

socket.getservbyname(servicename[, protocolname])

将 Internet 服务名称和协议名称转换为该服务的端口号。协议名称是可选的,如果提供的话应为 'tcp''udp',否则将匹配出所有协议。

socket.getservbyport(port[, protocolname])

将 Internet 端口号和协议名称转换为该服务的服务名称。协议名称是可选的,如果提供的话应为 'tcp''udp',否则将匹配出所有协议。

socket.ntohl(x)

将 32 位正整数从网络字节序转换为主机字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 4 字节交换操作。

socket.ntohs(x)

将 16 位正整数从网络字节序转换为主机字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 2 字节交换操作。

socket.htonl(x)

将 32 位正整数从主机字节序转换为网络字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 4 字节交换操作。

socket.htons(x)

将 16 位正整数从主机字节序转换为网络字节序。在主机字节序与网络字节序相同的计算机上,这是一个空操作。字节序不同将执行 2 字节交换操作。

socket.inet_aton(ip_string)

将 IPv4 地址从点分十进制字符串格式(如 ‘123.45.67.89’ )转换为 32 位压缩二进制格式,转换后为字节对象,长度为四个字符。与那些使用标准 C 库,且需要 struct in_addr 类型的对象的程序交换信息时,此功能很有用。 该类型即此函数返回的 32 位压缩二进制的 C 类型。

inet_aton() 也接受句点数少于三的字符串,详情请参阅 Unix 手册 inet(3)

如果传入本函数的 IPv4 地址字符串无效,则抛出 OSError。注意,具体什么样的地址有效取决于 inet_aton() 的底层 C 实现。

inet_aton() 不支持 IPv6,在 IPv4/v6 双协议栈下应使用 inet_pton() 来代替。

socket.inet_ntoa(packed_ip)

将 32 位压缩 IPv4 地址(一个 类字节对象,长 4 个字节)转换为标准的点分十进制字符串形式(如 ‘123.45.67.89’ )。与那些使用标准 C 库,且需要 struct in_addr 类型的对象的程序交换信息时,本函数很有用。 该类型即本函数参数中的 32 位压缩二进制数据的 C 类型。

如果传入本函数的字节序列长度不是 4 个字节,则抛出 OSErrorinet_ntoa() 不支持 IPv6,在 IPv4/v6 双协议栈下应使用 inet_ntop() 来代替。

在 3.5 版更改: 现在支持可写的 字节类对象

socket.inet_pton(address_family, ip_string)

将特定地址簇的 IP 地址(字符串)转换为压缩二进制格式。当库或网络协议需要接受 struct in_addr 类型的对象(类似 inet_aton() )或 struct in6_addr 类型的对象时,inet_pton() 很有用。

目前 address_family 支持 AF_INETAF_INET6。如果 IP 地址字符串 ip_string 无效,则抛出 OSError。注意,具体什么地址有效取决于 address_family 的值和 inet_pton() 的底层实现。

Availability: Unix (maybe not all platforms), Windows.

在 3.4 版更改: 添加了 Windows 支持

socket.inet_ntop(address_family, packed_ip)

将压缩 IP 地址(一个 类字节对象,数个字节长)转换为标准的、特定地址簇的字符串形式(如 '7.10.0.5''5aef:2b::8' )。当库或网络协议返回 struct in_addr 类型的对象(类似 inet_ntoa() )或 struct in6_addr 类型的对象时,inet_ntop() 很有用。

目前 address_family 支持 AF_INETAF_INET6。如果字节对象 packed_ip 与指定的地址簇长度不符,则抛出 ValueError。针对 inet_ntop() 调用的错误则抛出 OSError

Availability: Unix (maybe not all platforms), Windows.

在 3.4 版更改: 添加了 Windows 支持

在 3.5 版更改: 现在支持可写的 字节类对象

socket.CMSG_LEN(length)

返回给定 length 所关联数据的辅助数据项的总长度(不带尾部填充)。此值通常用作 recvmsg() 接收一个辅助数据项的缓冲区大小,但是 RFC 3542 要求可移植应用程序使用 CMSG_SPACE(),以此将尾部填充的空间计入,即使该项在缓冲区的最后。如果 length 超出允许范围,则抛出 OverflowError

Availability: most Unix platforms, possibly others.

3.3 新版功能.

socket.CMSG_SPACE(length)

返回 recvmsg() 所需的缓冲区大小,以接收给定 length 所关联数据的辅助数据项,带有尾部填充。接收多个项目所需的缓冲区空间是关联数据长度的 CMSG_SPACE() 值的总和。如果 length 超出允许范围,则抛出 OverflowError

请注意,某些系统可能支持辅助数据,但不提供本函数。还需注意,如果使用本函数的结果来设置缓冲区大小,可能无法精确限制可接收的辅助数据量,因为可能会有其他数据写入尾部填充区域。

Availability: most Unix platforms, possibly others.

3.3 新版功能.

socket.getdefaulttimeout()

返回用于新套接字对象的默认超时(以秒为单位的浮点数)。值 None 表示新套接字对象没有超时。首次导入 socket 模块时,默认值为 None

socket.setdefaulttimeout(timeout)

设置用于新套接字对象的默认超时(以秒为单位的浮点数)。首次导入 socket 模块时,默认值为 None。可能的取值及其各自的含义请参阅 settimeout()

socket.sethostname(name)

将计算机的主机名设置为 name。如果权限不足将抛出 OSError

Availability: Unix.

3.3 新版功能.

socket.if_nameindex()

返回一个列表,包含网络接口(网卡)信息二元组(整数索引,名称字符串)。系统调用失败则抛出 OSError

Availability: Unix.

3.3 新版功能.

socket.if_nametoindex(if_name)

返回网络接口名称相对应的索引号。如果没有所给名称的接口,则抛出 OSError

Availability: Unix.

3.3 新版功能.

socket.if_indextoname(if_index)

返回网络接口索引号相对应的接口名称。如果没有所给索引号的接口,则抛出 OSError

Availability: Unix.

3.3 新版功能.

18.1.3. 套接字对象

套接字对象具有以下方法。除了 makefile(),其他都与套接字专用的 Unix 系统调用相对应。

在 3.2 版更改: 添加了对 上下文管理器 协议的支持。退出上下文管理器与调用 close() 等效。

socket.accept()

接受一个连接。此 socket 必须绑定到一个地址上并且监听连接。返回值是一个 (conn, address) 对,其中 conn 是一个 的套接字对象,用于在此连接上收发数据,address 是连接另一端的套接字所绑定的地址。

新创建的套接字是 不可继承的

在 3.4 版更改: 该套接字现在是不可继承的。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.bind(address)

将套接字绑定到 address。套接字必须尚未绑定。( address 的格式取决于地址簇 —— 参见上文)

socket.close()

将套接字标记为关闭。当 makefile() 创建的所有文件对象都关闭时,底层系统资源(如文件描述符)也将关闭。一旦上述情况发生,将来对套接字对象的所有操作都会失败。对端将接收不到任何数据(清空队列数据后)。

垃圾回收时,套接字会自动关闭,但建议显式 close() 它们,或在它们周围使用 with 语句。

注解

close() 释放与连接相关联的资源,但不一定立即关闭连接。如果需要及时关闭连接,请在调用 close() 之前调用 shutdown()

socket.connect(address)

连接到 address 处的远程套接字。( address 的格式取决于地址簇 —— 参见上文)

如果连接被信号中断,则本方法将等待,直到连接完成。如果信号处理程序未抛出异常,且套接字阻塞中或已超时,则在超时后抛出 socket.timeout。对于非阻塞套接字,如果连接被信号中断,则本方法将抛出 InterruptedError 异常(或信号处理程序抛出的异常)。

在 3.5 版更改: 本方法现在将等待,直到连接完成,而不是在以下情况抛出 InterruptedError 异常。该情况为,连接被信号中断,信号处理程序未抛出异常,且套接字阻塞中或已超时(具体解释请参阅 PEP 475 )。

socket.connect_ex(address)

类似于 connect(address),但是对于 C 级别的 connect() 调用返回的错误,本函数将返回错误指示器,而不是抛出异常(对于其他问题,如“找不到主机”,仍然可以抛出异常)。如果操作成功,则错误指示器为 0,否则为 errno 变量的值。这对支持如异步连接很有用。

socket.detach()

将套接字对象置于关闭状态,而底层的文件描述符实际并不关闭。返回该文件描述符,使其可以重新用于其他目的。

3.2 新版功能.

socket.dup()

创建套接字的副本。

新创建的套接字是 不可继承的

在 3.4 版更改: 该套接字现在是不可继承的。

socket.fileno()

返回套接字的文件描述符(一个小整数),失败返回 -1。配合 select.select() 使用很有用。

在 Windows 下,此方法返回的小整数在允许使用文件描述符的地方无法使用(如 os.fdopen() )。Unix 无此限制。

socket.get_inheritable()

获取套接字文件描述符或套接字句柄的 可继承标志 :如果子进程可以继承套接字则为 True,否则为 False

3.4 新版功能.

socket.getpeername()

返回套接字连接到的远程地址。举例而言,这可以用于查找远程 IPv4/v6 套接字的端口号。(返回的地址格式取决于地址簇 —— 参见上文。)部分系统不支持此函数。

socket.getsockname()

返回套接字本身的地址。举例而言,这可以用于查找 IPv4/v6 套接字的端口号。(返回的地址格式取决于地址簇 —— 参见上文。)

socket.getsockopt(level, optname[, buflen])

返回指定套接字选项的值(参阅 Unix 手册页 getsockopt(2) )。所需的符号常量( SO_* 等)已定义在本模块中。如果未指定 buflen,则认为该选项值为整数,由本函数返回该整数值。如果指定 buflen,则它定义了用于存放选项值的缓冲区的最大长度,且该缓冲区将作为字节对象返回。对缓冲区的解码工作由调用者自行完成(针对编码为字节串的 C 结构,其解码方法请参阅可选的内置模块 struct )。

socket.gettimeout()

返回套接字操作相关的超时秒数(浮点数),未设置超时则返回 None。它反映最后一次调用 setblocking()settimeout() 后的设置。

socket.ioctl(control, option)
平台:Windows

ioctl() 方法是 WSAIoctl 系统接口的有限接口。请参考 Win32 文档 以获取更多信息。

在其他平台上,可以使用通用的 fcntl.fcntl()fcntl.ioctl() 函数,它们接受套接字对象作为第一个参数。

socket.listen([backlog])

启动一个服务器用于接受连接。如果指定 backlog,则它最低为 0(小于 0 会被置为 0),它指定系统允许暂未 accept 的连接数,超过后将拒绝新连接。未指定则自动设为合理的默认值。

在 3.5 版更改: backlog 参数现在是可选的。

socket.makefile(mode='r', buffering=None, *, encoding=None, errors=None, newline=None)

返回与套接字关联的 文件对象。返回的对象的具体类型取决于 makefile() 的参数。这些参数的解释方式与内置的 open() 函数相同,其中 mode 的值仅支持 'r' (默认),'w''b'

套接字必须处于阻塞模式,它可以有超时,但是如果发生超时,文件对象的内部缓冲区可能会以不一致的状态结尾。

关闭 makefile() 返回的文件对象不会关闭原始套接字,除非所有其他文件对象都已关闭且在套接字对象上调用了 socket.close()

注解

在 Windows 上,由 makefile() 创建的文件类对象无法作为带文件描述符的文件对象使用,如无法作为 subprocess.Popen() 的流参数。

socket.recv(bufsize[, flags])

从套接字接收数据。返回值是一个字节对象,表示接收到的数据。bufsize 指定一次接收的最大数据量。可选参数 flags 的含义请参阅 Unix 手册页 recv(2),它默认为零。

注解

为了最佳匹配硬件和网络的实际情况,bufsize 的值应为 2 的相对较小的幂,如 4096。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.recvfrom(bufsize[, flags])

从套接字接收数据。返回值是一对 (bytes, address),其中 bytes 是字节对象,表示接收到的数据,address 是发送端套接字的地址。可选参数 flags 的含义请参阅 Unix 手册页 recv(2),它默认为零。( address 的格式取决于地址簇 —— 参见上文)

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.recvmsg(bufsize[, ancbufsize[, flags]])

从套接字接收普通数据(至多 bufsize 字节)和辅助数据。ancbufsize 参数设置用于接收辅助数据的内部缓冲区的大小(以字节为单位),默认为 0,表示不接收辅助数据。可以使用 CMSG_SPACE()CMSG_LEN() 计算辅助数据缓冲区的合适大小,无法放入缓冲区的项目可能会被截断或丢弃。flags 参数默认为 0,其含义与 recv() 中的相同。

返回值是一个四元组: (data, ancdata, msg_flags, address)data 项是一个 bytes 对象,用于保存接收到的非辅助数据。ancdata 项是零个或多个元组 (cmsg_level, cmsg_type, cmsg_data) 组成的列表,表示接收到的辅助数据(控制消息):cmsg_levelcmsg_type 是分别表示协议级别和协议类型的整数,而 cmsg_data 是保存相关数据的 bytes 对象。msg_flags 项由各种标志按位或组成,表示接收消息的情况,详细信息请参阅系统文档。如果接收端套接字断开连接,则 address 是发送端套接字的地址(如果有),否则该值无指定。

某些系统上可以利用 AF_UNIX 套接字通过 sendmsg()recvmsg() 在进程之间传递文件描述符。使用此功能时(通常仅限于 SOCK_STREAM 套接字),recvmsg() 将在其辅助数据中返回以下格式的项 (socket.SOL_SOCKET, socket.SCM_RIGHTS, fds),其中 fds 是一个 bytes 对象,是新文件描述符表示为原生 C int 类型的二进制数组。如果 recvmsg() 在系统调用返回后抛出异常,它将首先关闭此机制接收到的所有文件描述符。

对于仅接收到一部分的辅助数据项,一些系统没有指示其截断长度。如果某个项目可能超出了缓冲区的末尾,recvmsg() 将发出 RuntimeWarning,并返回其在缓冲区内的部分,前提是该对象被截断于关联数据开始后。

在支持 SCM_RIGHTS 机制的系统上,下方的函数将最多接收 maxfds 个文件描述符,返回消息数据和包含描述符的列表(同时忽略意外情况,如接收到无关的控制消息)。另请参阅 sendmsg()

import socket, array

def recv_fds(sock, msglen, maxfds):
    fds = array.array("i")   # Array of ints
    msg, ancdata, flags, addr = sock.recvmsg(msglen, socket.CMSG_LEN(maxfds * fds.itemsize))
    for cmsg_level, cmsg_type, cmsg_data in ancdata:
        if (cmsg_level == socket.SOL_SOCKET and cmsg_type == socket.SCM_RIGHTS):
            # Append data, ignoring any truncated integers at the end.
            fds.fromstring(cmsg_data[:len(cmsg_data) - (len(cmsg_data) % fds.itemsize)])
    return msg, list(fds)

Availability: most Unix platforms, possibly others.

3.3 新版功能.

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.recvmsg_into(buffers[, ancbufsize[, flags]])

从套接字接收普通数据和辅助数据,其行为与 recvmsg() 相同,但将非辅助数据分散到一系列缓冲区中,而不是返回新的字节对象。buffers 参数必须是可迭代对象,它迭代出可供写入的缓冲区(如 bytearray 对象),这些缓冲区将被连续的非辅助数据块填充,直到数据全部写完或缓冲区用完为止。在允许使用的缓冲区数量上,操作系统可能会有限制( sysconf()SC_IOV_MAX 值)。ancbufsizeflags 参数的含义与 recvmsg() 中的相同。

返回值为四元组: (nbytes, ancdata, msg_flags, address),其中 nbytes 是写入缓冲区的非辅助数据的字节总数,而 ancdatamsg_flagsaddressrecvmsg() 中的相同。

示例:

>>> import socket
>>> s1, s2 = socket.socketpair()
>>> b1 = bytearray(b'----')
>>> b2 = bytearray(b'0123456789')
>>> b3 = bytearray(b'--------------')
>>> s1.send(b'Mary had a little lamb')
22
>>> s2.recvmsg_into([b1, memoryview(b2)[2:9], b3])
(22, [], 0, None)
>>> [b1, b2, b3]
[bytearray(b'Mary'), bytearray(b'01 had a 9'), bytearray(b'little lamb---')]

Availability: most Unix platforms, possibly others.

3.3 新版功能.

socket.recvfrom_into(buffer[, nbytes[, flags]])

从套接字接收数据,将其写入 buffer 而不是创建新的字节串。返回值是一对 (nbytes, address),其中 nbytes 是收到的字节数,address 是发送端套接字的地址。可选参数 flags 的含义请参阅 Unix 手册页 recv(2),它默认为零。( address 的格式取决于地址簇 —— 参见上文)

socket.recv_into(buffer[, nbytes[, flags]])

从套接字接收至多 nbytes 个字节,将其写入缓冲区而不是创建新的字节串。如果 nbytes 未指定(或指定为 0),则接收至所给缓冲区的最大可用大小。返回接收到的字节数。可选参数 flags 的含义请参阅 Unix 手册页 recv(2),它默认为零。

socket.send(bytes[, flags])

发送数据给套接字。本套接字必须已连接到远程套接字。可选参数 flags 的含义与上述 recv() 中的相同。本方法返回已发送的字节数。应用程序要负责检查所有数据是否已发送,如果仅传输了部分数据,程序需要自行尝试传输其余数据。有关该主题的更多信息,请参考 套接字编程指南

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendall(bytes[, flags])

发送数据给套接字。本套接字必须已连接到远程套接字。可选参数 flags 的含义与上述 recv() 中的相同。与 send() 不同,本方法持续从 bytes 发送数据,直到所有数据都已发送或发生错误为止。成功后会返回 None。出错后会抛出一个异常,此时并没有办法确定成功发送了多少数据。

在 3.5 版更改: 每次成功发送数据后,套接字超时不再重置。现在,套接字超时是发送所有数据的最大总持续时间。

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendto(bytes, address)
socket.sendto(bytes, flags, address)

发送数据给套接字。本套接字不应连接到远程套接字,而应由 address 指定目标套接字。可选参数 flags 的含义与上述 recv() 中的相同。本方法返回已发送的字节数。( address 的格式取决于地址簇 —— 参见上文。)

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendmsg(buffers[, ancdata[, flags[, address]]])

将普通数据和辅助数据发送给套接字,将从一系列缓冲区中收集非辅助数据,并将其拼接为一条消息。buffers 参数指定的非辅助数据应为可迭代的 字节类对象 (如 bytes 对象),在允许使用的缓冲区数量上,操作系统可能会有限制( sysconf()SC_IOV_MAX 值)。ancdata 参数指定的辅助数据(控制消息)应为可迭代对象,迭代出零个或多个 (cmsg_level, cmsg_type, cmsg_data) 元组,其中 cmsg_levelcmsg_type 是分别指定协议级别和协议类型的整数,而 cmsg_data 是保存相关数据的字节类对象。请注意,某些系统(特别是没有 CMSG_SPACE() 的系统)可能每次调用仅支持发送一条控制消息。flags 参数默认为 0,与 send() 中的含义相同。如果 address 指定为除 None 以外的值,它将作为消息的目标地址。返回值是已发送的非辅助数据的字节数。

在支持 SCM_RIGHTS 机制的系统上,下方的函数通过一个 AF_UNIX 套接字来发送文件描述符列表 fds。另请参阅 recvmsg()

import socket, array

def send_fds(sock, msg, fds):
    return sock.sendmsg([msg], [(socket.SOL_SOCKET, socket.SCM_RIGHTS, array.array("i", fds))])

Availability: most Unix platforms, possibly others.

3.3 新版功能.

在 3.5 版更改: 如果系统调用被中断,但信号处理程序没有触发异常,此方法现在会重试系统调用,而不是触发 InterruptedError 异常 (原因详见 PEP 475)。

socket.sendfile(file, offset=0, count=None)

使用高性能的 os.sendfile 发送文件,直到达到文件的 EOF 为止,返回已发送的字节总数。file 必须是一个以二进制模式打开的常规文件对象。如果 os.sendfile 不可用(如 Windows)或 file 不是常规文件,将使用 send() 代替。offset 指示从哪里开始读取文件。如果指定了 count,它确定了要发送的字节总数,而不会持续发送直到达到文件的 EOF。返回时或发生错误时,文件位置将更新,在这种情况下,file.tell() 可用于确定已发送的字节数。套接字必须为 SOCK_STREAM 类型。不支持非阻塞的套接字。

3.5 新版功能.

socket.set_inheritable(inheritable)

设置套接字文件描述符或套接字句柄的 可继承标志

3.4 新版功能.

socket.setblocking(flag)

设置套接字为阻塞或非阻塞模式:如果 flag 为 false,则将套接字设置为非阻塞,否则设置为阻塞。

本方法是某些 settimeout() 调用的简写:

  • sock.setblocking(True) 相当于 sock.settimeout(None)
  • sock.setblocking(False) 相当于 sock.settimeout(0.0)
socket.settimeout(value)

为阻塞套接字的操作设置超时。value 参数可以是非负浮点数,表示秒,也可以是 None。如果赋为一个非零值,那么如果在操作完成前超过了超时时间 value,后续的套接字操作将抛出 timeout 异常。如果赋为 0,则套接字将处于非阻塞模式。如果指定为 None,则套接字将处于阻塞模式。

更多信息请查阅 关于套接字超时的说明

socket.setsockopt(level, optname, value)

Set the value of the given socket option (see the Unix manual page setsockopt(2)). The needed symbolic constants are defined in the socket module (SO_* etc.). The value can be an integer or a bytes-like object representing a buffer. In the latter case it is up to the caller to ensure that the bytestring contains the proper bits (see the optional built-in module struct for a way to encode C structures as bytestrings).

在 3.5 版更改: 现在支持可写的 字节类对象

socket.shutdown(how)

关闭一半或全部的连接。如果 howSHUT_RD,则后续不再允许接收。如果 howSHUT_WR,则后续不再允许发送。如果 howSHUT_RDWR,则后续的发送和接收都不允许。

socket.share(process_id)

复制套接字,并准备将其与目标进程共享。目标进程必须以 process_id 形式提供。然后可以利用某种形式的进程间通信,将返回的字节对象传递给目标进程,还可以使用 fromshare() 在新进程中重新创建套接字。一旦本方法调用完毕,就可以安全地将套接字关闭,因为操作系统已经为目标进程复制了该套接字。

Availability: Windows.

3.3 新版功能.

注意此处没有 read()write() 方法,请使用不带 flags 参数的 recv()send() 来替代。

套接字对象还具有以下(只读)属性,这些属性与传入 socket 构造函数的值相对应。

socket.family

套接字的协议簇

socket.type

套接字的类型

socket.proto

套接字的协议。

18.1.4. 关于套接字超时的说明

一个套接字对象可以处于以下三种模式之一:阻塞、非阻塞或超时。套接字默认以阻塞模式创建,但是可以调用 setdefaulttimeout() 来更改。

  • blocking mode (阻塞模式)中,操作将阻塞,直到操作完成或系统返回错误(如连接超时)。
  • non-blocking mode (非阻塞模式)中,如果操作无法立即完成,则操作将失败(不幸的是,不同系统返回的错误不同):位于 select 中的函数可用于了解套接字何时以及是否可以读取或写入。
  • timeout mode (超时模式)下,如果无法在指定的超时内完成操作(抛出 timeout 异常),或如果系统返回错误,则操作将失败。

注解

在操作系统层面上,超时模式 下的套接字在内部都设置为非阻塞模式。同时,阻塞和超时模式在文件描述符和套接字对象之间共享,这些描述符和对象均应指向同一个网络端点。如果,比如你决定使用套接字的 fileno(),这一实现细节可能导致明显的结果。

18.1.4.1. 超时与 connect 方法

connect() 操作也受超时设置的约束,通常建议在调用 connect() 之前调用 settimeout(),或将超时参数直接传递给 create_connection()。但是,无论 Python 套接字超时设置如何,系统网络栈都有可能返回自带的连接超时错误。

18.1.4.2. 超时与 accept 方法

如果 getdefaulttimeout() 的值不是 None,则 accept() 方法返回的套接字将继承该超时值。若是 None,返回的套接字行为取决于侦听套接字的设置:

  • 如果侦听套接字处于 阻塞模式超时模式,则 accept() 返回的套接字处于 阻塞模式
  • 如果侦听套接字处于 非阻塞模式,那么 accept() 返回的套接字是阻塞还是非阻塞取决于操作系统。如果要确保跨平台时的正确行为,建议手动覆盖此设置。

18.1.5. 示例

以下是 4 个使用 TCP/IP 协议的最小示例程序:一台服务器,它将收到的所有数据原样返回(仅服务于一个客户端),还有一个使用该服务器的客户端。注意,服务器必须按序执行 socket(), bind(), listen(), accept() (可能需要重复执行 accept() 以服务多个客户端),而客户端仅需要按序执行 socket(), connect()。还须注意,服务器不在侦听套接字上发送 sendall()/recv(),而是在 accept() 返回的新套接字上发送。

前两个示例仅支持 IPv4。

# Echo server program
import socket

HOST = ''                 # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind((HOST, PORT))
    s.listen(1)
    conn, addr = s.accept()
    with conn:
        print('Connected by', addr)
        while True:
            data = conn.recv(1024)
            if not data: break
            conn.sendall(data)
# Echo client program
import socket

HOST = 'daring.cwi.nl'    # The remote host
PORT = 50007              # The same port as used by the server
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.connect((HOST, PORT))
    s.sendall(b'Hello, world')
    data = s.recv(1024)
print('Received', repr(data))

下两个例子与上两个很像,但是同时支持 IPv4 和 IPv6。 服务端将监听第一个可用的地址族(它本应同时监听两个)。 在大多数支持 IPv6 的系统上,IPv6 将有优先权并且服务端可能不会接受 IPv4 流量。 客户端将尝试连接到作为名称解析结果被返回的所有地址,并将流量发送给连接成功的第一个地址。

# Echo server program
import socket
import sys

HOST = None               # Symbolic name meaning all available interfaces
PORT = 50007              # Arbitrary non-privileged port
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC,
                              socket.SOCK_STREAM, 0, socket.AI_PASSIVE):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
    except OSError as msg:
        s = None
        continue
    try:
        s.bind(sa)
        s.listen(1)
    except OSError as msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print('could not open socket')
    sys.exit(1)
conn, addr = s.accept()
with conn:
    print('Connected by', addr)
    while True:
        data = conn.recv(1024)
        if not data: break
        conn.send(data)
# Echo client program
import socket
import sys

HOST = 'daring.cwi.nl'    # The remote host
PORT = 50007              # The same port as used by the server
s = None
for res in socket.getaddrinfo(HOST, PORT, socket.AF_UNSPEC, socket.SOCK_STREAM):
    af, socktype, proto, canonname, sa = res
    try:
        s = socket.socket(af, socktype, proto)
    except OSError as msg:
        s = None
        continue
    try:
        s.connect(sa)
    except OSError as msg:
        s.close()
        s = None
        continue
    break
if s is None:
    print('could not open socket')
    sys.exit(1)
with s:
    s.sendall(b'Hello, world')
    data = s.recv(1024)
print('Received', repr(data))

下面的例子演示了如何在 Windows 上使用原始套接字编写一个非常简单的网络嗅探器。 这个例子需要管理员权限来修改接口:

import socket

# the public network interface
HOST = socket.gethostbyname(socket.gethostname())

# create a raw socket and bind it to the public interface
s = socket.socket(socket.AF_INET, socket.SOCK_RAW, socket.IPPROTO_IP)
s.bind((HOST, 0))

# Include IP headers
s.setsockopt(socket.IPPROTO_IP, socket.IP_HDRINCL, 1)

# receive all packages
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_ON)

# receive a package
print(s.recvfrom(65565))

# disabled promiscuous mode
s.ioctl(socket.SIO_RCVALL, socket.RCVALL_OFF)

The last example shows how to use the socket interface to communicate to a CAN network using the raw socket protocol. To use CAN with the broadcast manager protocol instead, open a socket with:

socket.socket(socket.AF_CAN, socket.SOCK_DGRAM, socket.CAN_BCM)

在绑定 (CAN_RAW) 或连接 (CAN_BCM) socket 之后,你将可以在 socket 对象上正常使用 socket.send() 以及 socket.recv() 操作(及同类操作)。

This example might require special privileges:

import socket
import struct


# CAN frame packing/unpacking (see 'struct can_frame' in <linux/can.h>)

can_frame_fmt = "=IB3x8s"
can_frame_size = struct.calcsize(can_frame_fmt)

def build_can_frame(can_id, data):
    can_dlc = len(data)
    data = data.ljust(8, b'\x00')
    return struct.pack(can_frame_fmt, can_id, can_dlc, data)

def dissect_can_frame(frame):
    can_id, can_dlc, data = struct.unpack(can_frame_fmt, frame)
    return (can_id, can_dlc, data[:can_dlc])


# create a raw socket and bind it to the 'vcan0' interface
s = socket.socket(socket.AF_CAN, socket.SOCK_RAW, socket.CAN_RAW)
s.bind(('vcan0',))

while True:
    cf, addr = s.recvfrom(can_frame_size)

    print('Received: can_id=%x, can_dlc=%x, data=%s' % dissect_can_frame(cf))

    try:
        s.send(cf)
    except OSError:
        print('Error sending CAN frame')

    try:
        s.send(build_can_frame(0x01, b'\x01\x02\x03'))
    except OSError:
        print('Error sending CAN frame')

如果多次运行示例,且两次运行间隔很短,可能引发此错误:

OSError: [Errno 98] Address already in use

这是因为前一次运行使套接字处于 TIME_WAIT 状态,无法立即重用。

要防止这种情况,需要设置一个 socket 标志 socket.SO_REUSEADDR:

s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((HOST, PORT))

SO_REUSEADDR 标志告诉内核将处于 TIME_WAIT 状态的本地套接字重新使用,而不必等到固有的超时到期。

参见

关于套接字编程(C 语言)的介绍,请参阅以下文章:

  • An Introductory 4.3BSD Interprocess Communication Tutorial,作者 Stuart Sechrest
  • An Advanced 4.3BSD Interprocess Communication Tutorial,作者 Samuel J. Leffler et al,

两篇文章都在 UNIX 开发者手册,补充文档 1(第 PS1:7 和 PS1:8 节)中。那些特定于平台的参考资料,它们包含与套接字有关的各种系统调用,也是套接字语义细节的宝贵信息来源。对于 Unix,请参考手册页。对于 Windows,请参阅 WinSock(或 Winsock 2)规范。如果需要支持 IPv6 的 API,读者可能希望参考 RFC 3493,标题为 Basic Socket Interface Extensions for IPv6。