Python之底层级的网络接口——Socket(套接字)协议族及函数介绍
一、Socket
简介
Socket
(套接字)是网络通信中的基础概念,在Python
中借助socket
模块得以实现。通俗来讲,Socket
如同一个通信的端点,能让不同的程序或设备在网络中实现通信。
Socket
能够基于各种协议,常见的有TCP
(传输控制协议)和UDP
(用户数据报协议)。TCP
套接字提供了可靠且面向连接的通信模式,能够确保数据有序且无差错地传输。而UDP
套接字属于无连接、不可靠的方式,但具备更快的传输速率和更低的开销,适用于对实时性有较高要求、能够容忍一定数据丢失的情形。
在Python
里,运用socket
模块创建Socket
后,可以执行诸如绑定地址与端口、监听连接、建立连接、发送和接收数据等操作,进而达成各类网络应用,例如网络服务器、客户端程序、网络数据采集等。
二、Socket Families
(套接字协议族)
Socket Families
(套接字家族)决定了套接字所使用的地址格式和通信范围。特定的套接字对象需要的地址格式将根据此套接字对象被创建时指定的地址族被自动选择。就是当你创建一个套接字对象时,比如使用socket.socket()
函数,并指定了套接字的地址族(比如AF_INET
或 AF_INET6
等),那么后续在为这个套接字设置地址或者进行与地址相关的操作时,所需要的地址格式会根据您最初指定的地址族自动适配。注意:协议的地址格式数据类型均为元组类型,个别除外。
下边列出了Socket
常用协议常量。
协议名称 | 地址格式 | 参数介绍 | 功能描述 |
---|---|---|---|
AF_INET | (host, port) | host :是一个表示互联网域名标记形式的主机名的字符串,例如'daring.cwi.nl' 或者IPv4 地址,例如'100.50.200.5' 。port :是端口号,是一个整数值。用于区分同一主机上不同的网络服务或应用程序。 | 最常用的套接字,用于IPv4 地址。有两种可接受的特殊形式被用来代替一个主机地址:'' 代表INADDR_ANY (任意地址),套接字会绑定到本地机器的所有网络接口,从而能够接收来自任何接口的连接或数据;字符串'<broadcast>' 代表INADDR_BROADCAST (广播地址),用于进行广播操作。这些特殊形式在IPv6 中是不兼容的。如果您编写的Python 程序需要同时支持IPv4 和IPv6 ,或者主要侧重于IPv6 ,那么最好避免使用这些特定于IPv4 的特殊形式,以确保程序在不同的网络环境中都能正常工作。 |
AF_INET6 | (host,port,flowinfo,scope_id) | host :这代表了主机的地址,可以是IPv6 地址的字符串表示形式,例如2001:0db8:85a3:0000:0000:8a2e:0370:7334 。它用于标识网络中特定的主机。port :端口号。端口用于区分同一主机上不同的网络服务或应用程序。例如,常见的 HTTP 服务通常使用 80 端口,SSH 服务通常使用 22 端口等。flowinfo :这个参数与IPv6 数据包的流标签有关。流标签可以用于为特定类型的数据流提供特殊的处理,比如优先级或路由控制,但在实际应用中使用相对较少。scope_id :也称为作用域ID 。IPv6 地址的范围可能不同,比如链路本地、站点本地等。scope_id 用于明确地址的作用范围,帮助正确地路由和处理数据包。 | 处理 IPv6 地址和相关的网络通信。地址中后两个参数可以省略。能够处理IPv6 中的多播和广播操作,实现一对多或一对全体的通信方式。 |
AF_UNIX | "xxx/xxx/xx" | 一个绑定在文件系统节点上的AF_UNIX 套接字的地址表示为一个字符串。 | 主要用于同一台计算机上的进程间通信。高效的本地通信:因为数据不需要通过网络进行传输,所以通信速度快、效率高,并且开销小。基于文件系统:使用文件系统中的路径名来标识通信端点,类似于通过文件进行通信。适用于进程间协作:常用于需要在同一台机器上的不同进程之间快速、安全地交换数据和信息的场景,例如服务器进程与多个工作进程之间的通信。简单可靠:不需要处理网络相关的复杂性,如IP 地址、端口号、网络错误等。 |
AF_INET
是最常用的协议,主要用于不同主机之间通过网络进行通信,但也可以用于同一台主机不同进程之间的通信。然而,在同一台主机上不同进程间通信的场景中,通常更推荐使用AF_UNIX
套接字,因为它相比AF_INET
具有更低的开销和更高的效率,毕竟不需要经过网络协议栈的处理。但如果您一定要使用AF_INET
在同一台主机的不同进程间通信,也是可行的,只是可能不是最优选择。
下边列出Socket
的其它协议常量,可略过,也可作为了解。
协议名称 | 地址格式 | 参数介绍 | 功能描述 | |
---|---|---|---|---|
AF_BLUETOOTH | BTPROTO_L2CAP | (bdaddr, psm) | bdaddr :蓝牙设备的地址,以字符串形式表示,用于明确通信的目标设备。psm :协议服务多路复用器(Protocol Service Multiplexer)的值,是一个整数。它用于标识不同的上层协议或服务,帮助L2CAP 层对数据进行正确的路由和处理。 | 逻辑链路控制和适配协议(Logical Link Control and Adaptation Protocol),主要为上层协议提供面向连接和无连接的数据服务,实现不同类型数据的封装、分段和重组,支持协议的多路复用等功能。 |
BTPROTO_RFCOMM | (bdaddr, channel) | bdaddr :蓝牙设备的地址,以字符串形式给出,指定通信的对方设备。channel :整数形式的通道号。RFCOMM 基于蓝牙底层链路,通过不同的通道号来区分不同的逻辑连接,实现多个应用在同一蓝牙链路上的并发通信。 | 射频通信协议(Radio Frequency Communication Protocol),它提供了基于蓝牙的串行端口仿真,常用于在蓝牙设备之间建立类似于传统串行通信的连接,适用于诸如蓝牙鼠标、键盘等设备的数据传输。 | |
BTPROTO_HCI | (device_id,) | device_id :根据系统的不同,可以是整数或字符串。在NetBSD 和DragonFlyBSD 系统中需要是蓝牙地址字符串,而在其他系统中需要是整数。它用于标识特定的蓝牙接口或设备,使主机能够准确地与指定的蓝牙硬件进行交互。 | 主机控制接口协议(Host Controller Interface Protocol),定义了主机(如计算机)与蓝牙控制器(通常是蓝牙硬件模块)之间的通信接口和命令格式,用于主机对蓝牙设备的控制和配置。 | |
BTPROTO_SCO | bdaddr | bdaddr :以字节形式存储的蓝牙设备地址。由于是字节对象,更便于在底层进行数据处理和传输,用于指定语音通信的目标设备。 | 同步面向连接协议(Synchronous Connection-Oriented),主要用于支持蓝牙语音通信等需要实时、同步和连续数据流的应用。 | |
AF_NETLINK | (pid, groups) | groups :指定要接收的多播组。pid :进程ID ,用于标识发送或接收消息的进程。 | 一种特殊的地址族,用于内核与用户空间之间的通信。常用于内核向用户空间应用程序发送通知,或者用户空间应用程序向内核请求信息和配置内核模块等操作。 | |
AF_TIPC | (addr_type, v1, v2, v3 [, scope]) | addr_type :取TIPC_ADDR_NAMESEQ 、TIPC_ADDR_NAME 或TIPC_ADDR_ID 中的一个。scope :取TIPC_ZONE_SCOPE 、TIPC_CLUSTER_SCOPE 和TIPC_NODE_SCOPE 中的一个。如果 addr_type 为TIPC_ADDR_NAME ,那么v1 是服务器类型,v2 是端口标识符,v3 应为0 。如果 addr_type 为TIPC_ADDR_NAMESEQ ,那么v1 是服务器类型,v2 是端口号下限,而v3 是端口号上限。如果 addr_type 为TIPC_ADDR_ID ,那么v1 是节点(node) ,v2 是ref ,v3 应为0 。 | 一种专门设计用于进程间通信的地址族。应用于大规模的分布式计算系统,其中多个进程需要高效地交换数据和控制信息。 | |
AF_CAN | CAN_ISOTP | (interface, rx_addr, tx_addr) | interface :是表示网络接口名称的字符串,如'can0' 。网络接口名'' 可以用于接收本族所有网络接口的数据包。两个额外参数都是无符号长整数,都表示CAN 标识符(标准或扩展标识符)。 | AF_CAN :表示控制器局域网络(Controller Area Network,CAN)协议。广泛应用于汽车、工业自动化等领域的串行通信协议。CAN ISOTP :能够在CAN 帧的基础上实现较大数据量的传输。CAN 帧本身的数据长度有限,而CAN ISOTP 可以将大数据分割成多个CAN 帧进行传输,并在接收端重新组合。CAN J1939 是一种基于CAN 总线的应用层协议,主要用于商用车(如卡车、客车、农业和建筑机械等)的车载网络通信。 |
CAN_J1939 | (interface, name, pgn, addr) | 额外参数有:表示ECU 名称的64 位无符号整数,表示参数组号 (Parameter Group Number, PGN) 的32 位无符号整数,以及表示地址的8 位整数。 | ||
AF_ALG | (type, name [, feat [, mask]]) | type 是表示算法类型的字符串,如aead 、hash 、skcipher 或rng 。name 是表示算法类型和操作模式的字符串,如sha256 、hmac(sha256) 、cbc(aes) 或 drbg_nopr_ctr_aes256 。feat 和mask 是无符号32 位整数。 | AF_ALG 是一个仅Linux 可用的、基于套接字的接口,用于连接内核加密算法。通过AF_ALG ,内核可以管理加密密钥、执行加密和解密操作、处理认证相关的任务等,从而减轻应用程序的负担,并提供更高效和安全的网络通信。 | |
AF_VSOCK | (CID, port) | CID (Context ID):用于标识通信的上下文。它类似于一个区域或范围的标识符,有助于区分不同的通信场景或环境。Port (端口):指定通信的端口号,类似于传统网络通信中的端口概念,用于区分不同的服务或应用程序。 | 一种用于虚拟机(VM)和主机之间通信的地址族。 | |
AF_PACKET | (ifname, proto[, pkttype[, hatype[, addr]]]) | ifname :指定设备名称的字符串。proto :以太网协议号。 可以为ETH_P_ALL 表示捕获所有协议,某个ETHERTYPE_* 常量 或者任何其他以太网协议号。pkttype :指定数据包类型的整数(可选):
hatype :可选整数,指定 ARP 硬件地址类型。addr :可选的类字节串对象,用于指定硬件物理地址,其解释取决于各设备。 | 一个直接连接网络设备的低层级接口。用于在Linux 系统中直接访问链路层的数据。 | |
AF_QIPCRTR | (node, port) | node :表示特定节点标识,用于指定要与之通信的协处理器或服务所在的位置或标识。port :是通信端口,用于确定在特定节点上进行通信的具体通道或服务入口,类似于常见网络通信中端口的作用,不同的端口对应不同的服务或功能。 | 是一个仅Linux 可用的、基于套接字的接口,用于与高通平台中协处理器上运行的服务进行通信。 | |
IPPROTO_UDPLITE | self.setsockopt(IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV, length) 修改传出数据包的哪一部分计算入校验码内,而self.setsockopt(IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV, length) 将过滤掉计算入校验码的数据太少的数据包。在这两种情况下,length 都应在range(8, 2**16, 8) 范围内。 | 是一种UDP 的变体,允许指定数据包的哪一部分计算入校验码内。它添加了两个可以修改的套接字选项。 | ||
AF_HYPERV | (vm_id, service_id) | vm_id 为虚拟机标识号或者如果目标不是一台特定的虚拟机则为已知VMID 值的集合。 在socket 上定义的已知VMID 常量有:
service_id 是已注册服务的服务标识号。 | Windows 专属的用于同Hyper-V 主机和客户机通信的基于套接字的接口。 |
三、Socket
(套接字)函数
3.1 创建Socket
(套接字)函数
下列函数都能创建套接字对象。
函数 | 参数 | 描述 |
---|---|---|
class socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None) | family :指定套接字的地址族,常见的有 AF_INET (IPv4)、AF_INET6 (IPv6)、AF_UNIX 等。type :指定套接字的类型,如SOCK_STREAM (流式套接字,通常用于TCP 协议)、SOCK_DGRAM (数据报套接字,通常用于UDP 协议)、SOCK_RAW (表示原始套接字,可以直接访问底层协议(如IP 协议),用于更底层的网络编程和数据包处理)等。proto :指定协议号,可以设置为0 来使用默认的协议。但在某些特定的地址族(如AF_CAN )中,需要指定具体的协议,例如CAN_RAW 、CAN_BCM 、CAN_ISOTP 或CAN_J1939 等。如果不指定具体的协议号或者指定为0 ,系统会根据所选择的地址族和套接字类型自动选择合适的默认协议。fileno :如果指定,会根据给定的文件描述符自动检测family ,type 和proto 的值,但显式传入的参数可以覆盖自动检测的值。 | 使用给定的地址族、套接字类型和协议号创建一个新的套接字。新创建的套接字是不可继承的。 |
socket.socketpair([family[, type[, proto]]]) | 参数可选。同上述参数。 | 使用给定的地址族、套接字类型和协议号构建一对已连接的套接字对象。创建的这对套接字在创建时就已经相互连接,数据可以在它们之间进行双向传输。可以用于在同一进程内的不同部分之间进行通信。新创建的套接字都是不可继承的。 |
socket.create_connection(address, timeout=GLOBAL_DEFAULT, source_address=None, *, all_errors=False) | address :这是一个表示连接目标的元组,格式通常为(host, port) ,其中host 是主机名或IP 地址,port 是端口号。timeout :指定连接尝试的超时时间。如果在指定的时间内无法建立连接,将引发超时异常。如果未指定,将使用全局默认的超时值。source_address :可选参数,用于指定连接的源地址(本地的 IP 地址和端口)。all_errors :一个布尔值。如果为True ,则在发生错误时,返回所有可能的错误信息;如果为False (默认值),则只返回第一个遇到的错误。 | 用于创建一个到指定地址的连接。该函数会自动尝试解析主机名,并根据指定的地址族(通常首先尝试IPv4 ,然后IPv6 )建立连接。如果连接成功,将返回一个套接字对象,可用于后续的数据通信。如果连接失败,将根据超时设置和错误情况抛出相应的异常。 |
socket.create_server(address, *, family=AF_INET, backlog=None, reuse_port=False, dualstack_ipv6=False) | address 和family 参数同上,family 应当为 AF_INET 或AF_INET6 。backlog :传递给socket.listen() 的队列大小;当未指定时,将选择一个合理的默认值。reuse_port :布尔值,如果为True ,在支持的系统上启用端口复用。dualstack_ipv6 : 布尔值,如果为True ,且平台支持,则套接字能接受IPv4 和IPv6 连接,否则将抛出ValueError 异常。该参数可以与 has_dualstack_ipv6() 结合使用。 | 创建一个绑定到 address (一个 (host, port) 元组)的TCP 套接字并返回该套接字对象的便捷函数。 |
socket.has_dualstack_ipv6() | —— | 用于检查当前运行的平台是否支持创建能够同时处理IPv4 和IPv6 连接的TCP 套接字。如果支持,函数返回True ;否则返回 False 。 |
socket.fromfd(fd, family, type, proto=0) | fd :这是一个有效的文件描述符(由文件对象的fileno() 方法返回的整数) ,通常是从其他与文件或套接字操作相关的系统调用中获取的。family :指定套接字的地址族,如AF_INET 、AF_INET6 等。type :指定套接字的类型,如SOCK_STREAM 、SOCK_DGRAM 等。proto :指定套接字使用的协议号,默认值为0 。 | 用于从现有的文件描述符创建一个套接字对象。本函数的使用场景相对较少。然而,当需要将套接字作为标准输入或输出传递给程序(例如Unix 中由inet 进程启动的服务器)时,它能够发挥作用,用于获取或设置套接字的相关选项。需要注意的是,通过此函数创建的套接字会被默认假定为处于阻塞模式。 |
socket.fromshare(data) | data 通常是之前通过socket.share() 方法保存的套接字相关的数据。通过socket.fromshare() 可以重新构建出对应的套接字对象,以便继续进行套接字相关的操作。 | 用于从给定的数据中恢复套接字对象。通过此函数创建的套接字会被默认假定为处于阻塞模式。 |
socket.SocketType | 这是一个Python 类型对象,表示套接字对象的类型。它等同于type(socket(...)) 。 | 用于表示套接字类型的对象。可以通过比较一个套接字对象的类型和socket.SocketType 来确认该套接字是否为特定的类型。 |
3.2 其它函数
函数 | 参数 | 描述 |
---|---|---|
socket.close(fd) | fd :这是一个有效的文件描述符(由文件对象的fileno() 方法返回的整数) ,通常是从其他与文件或套接字操作相关的系统调用中获取的。 | 关闭一个套接字文件描述符,以释放相关资源并结束与该套接字的连接。它类似于os.close() ,但专用于套接字。在某些平台上(特别是在Windows 上),os.close() 对套接字文件描述符无效。如果省略 fd 参数,则是用于关闭当前正在操作的套接字对象本身,并不针对某个特定的文件描述符fd 进行关闭操作。 |
socket.getaddrinfo(host, port, family=AF_UNSPEC, type=0, proto=0, flags=0) | host :要查询的主机名或 IP 地址。port :服务端口号,可以是字符串形式(如'http' )或数字形式。family :地址族,默认为AF_UNSPEC ,表示返回IPv4 和IPv6 的地址信息。常见的还有AF_INET (IPv4)和AF_INET6 (IPv6)。type :套接字类型,常见的有SOCK_STREAM (TCP和SOCK_DGRAM (UDP),默认为0 表示返回所有类型。proto :协议号,默认为0 表示返回所有协议。flags :控制标志,用于影响结果,常见的标志如AI_PASSIVE (用于服务器,当host 为空时返回适合监听的地址)。 | 此函数对下层系统的C 函数getaddrinfo 进行了包装。返回一个将host/port 参数转换为5 元组的序列,其中包含创建(连接到某服务的)套接字所需的所有参数。每个 5 元组的结构为(family, type, proto, canonname, sockaddr) :family、type、proto :是传递给 socket() 函数创建套接字的参数。canonname :如果指定了 AI_CANONNAME 标志且有规范名称,则为表示主机的规范名称,否则为空。sockaddr :是一个与地址族相关的元组,用于传递给socket.connect() 方法。 |
socket.getfqdn([name]) | name :用于指定您想要获取其完全限定域名的主机名或IP 地址。如果 name 为空或等于'0.0.0.0' ,那么函数会将其视为本地主机,就返回通过gethostname() 获取到的主机名,并尝试获取本地主机的FQDN 。 | 用于获取指定名称的完全限定域名(Fully Qualified Domain Name,FQDN )。它首先通过gethostbyaddr() 来获取主机名,如果这个主机名存在,就检查它是否是FQDN ,如果是就返回;如果不是,就检查主机的别名,找到第一个包含句点的名称作为FQDN 返回。 |
socket.gethostbyname(hostname) | hostname :主机名(域名),如果输入的hostname 本身就是一个有效的IPv4 地址,那么函数会直接返回这个地址,不进行任何转换。 | 用于将给定的主机名转换为IPv4 地址。需要注意的是,这个函数不支持IPv6 名称的解析,如果需要同时支持IPv4 和 IPv6 ,应该使用getaddrinfo() 函数。 |