socket网络编程基础

Python进阶编程专题 · Python套接字编程入门到实践

专题:Python进阶编程系统学习

关键词:Python, socket, TCP, UDP, 网络编程, 服务器, 客户端, 非阻塞IO, selectors

一、课程概述

Socket(套接字)是网络编程的基石,它提供了一种进程间通信的机制,允许数据在不同主机之间或同一主机的不同进程之间进行交换。Python标准库中的 socket 模块封装了底层操作系统提供的套接字接口,使得开发者能够以简洁、Pythonic的方式构建网络应用程序。本专题将系统讲解Python socket编程的核心概念、API用法和最佳实践,涵盖TCP/UDP协议、阻塞与非阻塞IO、异常处理、高级IO多路复用乃至一个完整HTTP服务器的实战实现。

适用场景:Web服务器开发、即时通讯系统、物联网设备通信、文件传输工具、网络爬虫、远程监控、游戏服务器等几乎所有涉及网络数据传输的应用领域。

二、网络编程基础概念

2.1 TCP与UDP协议

传输层协议是socket编程的核心。TCP(传输控制协议)是面向连接的协议,提供可靠的数据传输服务,确保数据包按序到达且不丢失,适用于对数据完整性要求高的场景,如文件传输、网页浏览。UDP(用户数据报协议)是无连接的协议,提供尽最大努力交付的服务,不保证数据包到达顺序和完整性,但延迟更低、开销更小,适用于实时通信场景,如视频直播、在线游戏、DNS查询。

特性TCPUDP
连接方式面向连接(三次握手)无连接
可靠性可靠传输、确认重传不可靠、尽最大努力
数据顺序保证有序到达不保证顺序
传输速度相对较慢(头部开销大)快速(头部开销小)
数据边界流式传输,无边界保留消息边界
典型应用HTTP、FTP、SMTP、SSHDNS、DHCP、VoIP、视频流

2.2 IP地址与端口号

IP地址标识网络中的主机(IPv4为32位地址如 127.0.0.1,IPv6为128位地址),端口号标识主机上的特定进程(16位无符号整数,范围 0~65535)。知名端口(0~1023)由系统服务占用,如HTTP的80端口、HTTPS的443端口、SSH的22端口。注册端口(1024~49151)可供用户应用程序使用,动态/私有端口(49152~65535)通常由客户端临时分配。

2.3 地址族与套接字类型

Python的 socket 模块支持多种地址族和套接字类型的组合,最常见的组合是 AF_INET(IPv4地址族)与 SOCK_STREAM(流式套接字,对应TCP)或 SOCK_DGRAM(数据报套接字,对应UDP)。此外,AF_INET6 用于IPv6、AF_UNIX 用于同一主机上的Unix域套接字通信。

# 常见地址族与套接字类型常量 import socket # 地址族 print(socket.AF_INET) # IPv4 地址族 (值为2) print(socket.AF_INET6) # IPv6 地址族 (值为23) print(socket.AF_UNIX) # Unix域套接字 (值为1) # 套接字类型 print(socket.SOCK_STREAM) # TCP 流式套接字 (值为1) print(socket.SOCK_DGRAM) # UDP 数据报套接字 (值为2) print(socket.SOCK_RAW) # 原始套接字 (值为3)

三、socket模块核心API详解

3.1 创建套接字:socket()

socket.socket(family=AF_INET, type=SOCK_STREAM, proto=0) 是最基本的工厂函数,用于创建新的套接字对象。family参数指定地址族,type参数指定套接字类型,proto通常默认为0由系统自动选择。创建后的套接字对象提供了丰富的实例方法来执行绑定、监听、连接、收发数据等操作。

# 创建TCP套接字 tcp_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 创建UDP套接字 udp_sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) # 使用with语句管理套接字生命周期(Python 3.6+) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: s.connect(('example.com', 80)) s.sendall(b'GET / HTTP/1.1\r\nHost: example.com\r\n\r\n') data = s.recv(4096) print(data.decode())

3.2 绑定地址:bind()

bind(address) 将套接字绑定到一个特定的IP地址和端口上。参数是一个二元组 (host, port)。host可以为空字符串表示绑定到所有可用接口,port指定监听端口。绑定操作通常在服务器端使用,在客户端程序中通常不需要显式调用bind,系统会自动分配临时端口。

# 绑定到本地所有网络接口的8080端口 server_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_sock.bind(('0.0.0.0', 8080)) # 绑定到回环地址(仅本机可访问) server_sock.bind(('127.0.0.1', 8080)) # 绑定到特定网卡IP server_sock.bind(('192.168.1.100', 8080))

3.3 监听连接:listen()

listen([backlog]) 使服务器套接字进入被动监听状态,准备接受客户端连接请求。backlog参数指定操作系统允许的最大挂起连接数(等待accept的连接队列长度),超过此数量的连接将被拒绝。backlog的具体上限取决于操作系统,通常设置为 5~128 之间。

3.4 接受连接:accept()

accept() 阻塞等待并接受一个客户端连接请求。它返回一个二元组 (client_socket, client_address),其中 client_socket 是新建的套接字对象用于与该客户端进行数据通信,client_address 是客户端的地址信息(IP和端口)。accept 是TCP服务器实现并发连接处理的核心入口。

# TCP服务器核心循环:监听 → 接受 → 处理 → 关闭 server_sock.listen(5) print('服务器已启动,等待连接...') while True: client_sock, addr = server_sock.accept() print(f'收到来自 {addr} 的连接') try: data = client_sock.recv(1024) if data: client_sock.sendall(b'HTTP/1.1 200 OK\r\n\r\nHello, World!') finally: client_sock.close()

3.5 连接服务器:connect() 与 connect_ex()

connect(address) 客户端向指定服务器地址发起TCP连接请求,连接成功或失败时返回(失败抛出异常)。connect_ex(address) 在连接失败时返回错误码而非抛出异常,类似于C语言中connect系统调用的返回方式,适合需要精细控制错误处理的场景。

3.6 发送数据:send()、sendall() 与 sendto()

send(bytes[, flags]) 发送TCP数据,返回实际发送的字节数,可能少于要发送的总字节数,需要循环发送直到全部发完。sendall(bytes[, flags]) 在内部循环调用 send 直到所有数据发送完毕或发生错误,开发者无需关心部分发送问题,推荐使用。sendto(bytes, address) 用于UDP套接字,在无连接的UDP通信中指定目标地址发送数据报。

# send 与 sendall 的对比 data = b'X' * 100000 # 10万字节 # send 可能需要多次调用才能发完 total_sent = 0 while total_sent < len(data): sent = sock.send(data[total_sent:]) if sent == 0: raise RuntimeError('连接中断') total_sent += sent # sendall 内部自动循环发送,简洁安全 sock.sendall(data) # 一行搞定全部发送

3.7 接收数据:recv() 与 recvfrom()

recv(bufsize[, flags]) 接收TCP数据,返回接收到的字节数据。bufsize 指定最大接收字节数,实际返回的数据长度可能小于此值。当对端关闭连接时,recv 返回空字节串 b'',这是检测连接关闭的标准方式。recvfrom(bufsize[, flags]) 用于UDP套接字,返回一个二元组 (data, address),其中 address 是发送方的地址信息。

# TCP接收循环:持续读取直到连接关闭 def recv_all(sock, bufsize=4096): """接收所有数据直到连接关闭,返回完整字节数据""" chunks = [] while True: chunk = sock.recv(bufsize) if not chunk: # 连接关闭 break chunks.append(chunk) return b''.join(chunks)

3.8 关闭套接字:close() 与 shutdown()

close() 释放套接字资源,在引用计数归零时真正关闭底层文件描述符。shutdown(how) 提供更精细的关闭控制:SHUT_RD 禁止后续读操作,SHUT_WR 禁止后续写操作(发送FIN包通知对端),SHUT_RDWR 同时禁止读写。shutdown 可以确保对端立即收到连接关闭通知,而 close 仅在引用计数归零时触发。

API使用口诀:服务器端:socket → bind → listen → accept → recv/send → close;客户端:socket → connect → send/recv → close。

四、TCP服务器与客户端完整实现

4.1 单线程TCP回显服务器

以下是一个完整的TCP服务器实现,它接收客户端发送的数据并将原数据返回给客户端(回显服务)。此版本为单线程顺序处理,一次只能服务一个客户端。

# tcp_echo_server.py - TCP回显服务器 import socket HOST = '127.0.0.1' PORT = 8888 BACKLOG = 5 BUFFER_SIZE = 1024 def start_server(): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_sock: # 允许地址重用,避免重启时"Address already in use"错误 server_sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server_sock.bind((HOST, PORT)) server_sock.listen(BACKLOG) print(f'[服务器] 已启动,监听 {HOST}:{PORT}') while True: print('[服务器] 等待客户端连接...') conn, addr = server_sock.accept() print(f'[服务器] 客户端已连接:{addr}') with conn: while True: data = conn.recv(BUFFER_SIZE) if not data: print(f'[服务器] 客户端 {addr} 已断开') break print(f'[服务器] 收到:{data.decode()}') conn.sendall(b'[ECHO] ' + data) print(f'[服务器] 已回显:{data.decode()}') if __name__ == '__main__': start_server()

4.2 TCP回显客户端

# tcp_echo_client.py - TCP回显客户端 import socket HOST = '127.0.0.1' PORT = 8888 def start_client(): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_sock: client_sock.connect((HOST, PORT)) print(f'[客户端] 已连接到服务器 {HOST}:{PORT}') while True: msg = input('[客户端] 请输入消息(输入quit退出):') if msg.lower() == 'quit': break client_sock.sendall(msg.encode()) response = client_sock.recv(1024) print(f'[客户端] 服务器回复:{response.decode()}') if __name__ == '__main__': start_client()

测试方法:先在一个终端中运行服务器程序,然后在另一个终端中运行客户端程序。客户端发送的消息会被服务器添加 [ECHO] 前缀后返回。输入 quit 即可退出客户端。

4.3 多线程TCP服务器

上述单线程版本只能同时为一个客户端服务。在生产环境中,服务器需要同时处理多个客户端连接。使用 threading 模块为每个客户端创建一个独立线程是最简单的并发方案。

# tcp_multithread_server.py - 多线程TCP服务器 import socket import threading HOST = '0.0.0.0' PORT = 8888 def handle_client(conn, addr): """处理单个客户端连接的函数,在独立线程中运行""" print(f'[线程] 新连接来自 {addr}') with conn: while True: data = conn.recv(1024) if not data: break conn.sendall(b'[SERVER] ' + data) print(f'[线程] 连接 {addr} 已关闭') def start_server(): with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind((HOST, PORT)) sock.listen() print(f'[服务器] 多线程服务器启动于 {HOST}:{PORT}') while True: conn, addr = sock.accept() # 每个连接启动一个独立线程处理 t = threading.Thread(target=handle_client, args=(conn, addr)) t.start() print(f'[服务器] 活动连接数:{threading.active_count() - 1}') if __name__ == '__main__': start_server()

五、UDP服务器与客户端完整实现

5.1 UDP服务器

UDP是无连接协议,服务器无需调用 listen 和 accept,直接 recvfrom 等待数据报到达,并通过 sendto 回复。由于无连接特性,UDP天然支持"多客户端并发访问",因为每个 recvfrom/sendto 都是独立的。

# udp_echo_server.py - UDP回显服务器 import socket HOST = '127.0.0.1' PORT = 9999 with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: sock.bind((HOST, PORT)) print(f'[UDP服务器] 已启动,监听 {HOST}:{PORT}') while True: data, addr = sock.recvfrom(1024) print(f'[UDP服务器] 收到来自 {addr} 的数据:{data.decode()}') sock.sendto(b'[UDP_ECHO] ' + data, addr) print(f'[UDP服务器] 已回复 {addr}')

5.2 UDP客户端

# udp_echo_client.py - UDP回显客户端 import socket HOST = '127.0.0.1' PORT = 9999 with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as sock: msg = 'Hello, UDP Server!' sock.sendto(msg.encode(), (HOST, PORT)) print(f'[UDP客户端] 已发送:{msg}') # UDP recvfrom 可能阻塞,建议设置超时防止无限等待 sock.settimeout(5.0) try: data, addr = sock.recvfrom(1024) print(f'[UDP客户端] 收到回复:{data.decode()}') except socket.timeout: print('[UDP客户端] 超时:服务器无响应')

UDP注意事项:UDP数据报大小受限于网络MTU(通常为1500字节),建议单次发送不超过 65507 字节(IPv4 UDP理论最大值)。UDP不保证数据送达,应用层需要自行实现确认重传机制(如需可靠传输)。

六、阻塞与非阻塞IO

6.1 默认阻塞模式

Python socket默认工作于阻塞模式。当调用 recv() 时,线程将挂起直到有数据到达;调用 accept() 时,线程将等待直到有新连接;调用 connect() 时,线程将阻塞直到连接建立或失败。阻塞模式编程模型简单直观,但在需要同时处理多个连接时必须使用多线程或多进程方案。

6.2 setblocking() 设置非阻塞

setblocking(flag) 控制套接字是否为阻塞模式。传入 False 将套接字切换为非阻塞模式,此时所有IO操作立即返回。如果没有数据可读,recv() 将抛出 BlockingIOError;如果没有连接待处理,accept() 同样抛出 BlockingIOError

# 非阻塞模式示例 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setblocking(False) # 切换为非阻塞模式 try: data = sock.recv(1024) except BlockingIOError: # 没有数据可读,在此处执行其他任务 print('当前没有数据可读,稍后重试')

6.3 settimeout() 设置超时

settimeout(value) 提供介于阻塞和非阻塞之间的折中方案。设置超时后,socket操作最多等待指定秒数,超时后抛出 socket.timeout 异常。超时值为 None 表示阻塞模式,0 表示非阻塞模式。超时模式在某些场景下比纯非阻塞更易用,因为它不需要频繁轮询。

# 超时模式示例 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(10.0) # 设置10秒超时 sock.connect(('example.com', 80)) # 最多等待10秒 try: data = sock.recv(1024) except socket.timeout: print('接收数据超时')
模式设置方式行为适用场景
阻塞 (默认)默认IO操作无限等待多线程服务器、简单客户端
非阻塞setblocking(False)IO操作立即返回,无数据时抛异常事件驱动框架、select/poll/epoll
超时settimeout(n)操作等待n秒后抛 timeuot 异常需要限时等待的客户端

七、Socket异常处理

7.1 socket异常层级

Python 的 socket 模块定义了一套完整的异常层次结构。正确地捕获和处理这些异常是编写健壮网络应用的关键。所有 socket 异常都继承自 OSError,但为了方便精确定位错误,最好使用更具体的异常类。

# 常见的socket异常及处理 import socket import errno def safe_connect(host, port, timeout=5): """带异常处理的健壮连接函数""" sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) try: sock.connect((host, port)) print(f'成功连接到 {host}:{port}') return sock except socket.gaierror as e: print(f'DNS解析失败 {host}: {e}') except socket.timeout as e: print(f'连接超时 {host}:{port}: {e}') except ConnectionRefusedError as e: print(f'连接被拒绝 {host}:{port}: {e}') except OSError as e: print(f'网络错误 {host}:{port}: (errno={e.errno}) {e.strerror}') return None

常见错误与排查:ConnectionRefusedError 表示目标端口未开放或防火墙阻止;gaierror 表示域名无法解析;timeout 表示网络不可达或服务器过载;BrokenPipeError 表示对端已关闭连接;ConnectionResetError 表示对端异常断开(如进程崩溃)。

7.2 优雅关闭检测

TCP连接关闭检测在网络编程中尤为重要。recv 返回空字节串是优雅关闭的信号。但在实际场景中,网络异常可能导致连接半开状态而 recv 不会返回空字节串。建议结合心跳机制(heartbeat)定期探测连接健康状态,确保及时释放无效连接资源。

# 带心跳检测的接收循环 import select def recv_with_heartbeat(sock, heartbeat_interval=30): """使用selectors实现带心跳检测的数据接收""" sock.settimeout(heartbeat_interval) while True: try: data = sock.recv(4096) if not data: print('连接已优雅关闭') return None yield data except socket.timeout: # 长时间无数据,发送心跳探测 try: sock.sendall(b'\x00') # 心跳包 except (BrokenPipeError, ConnectionResetError): print('连接已断开') return None

八、Socket选项配置

8.1 SO_REUSEADDR -- 地址重用

SO_REUSEADDR 是最常用的socket选项之一,它允许服务器在 TIME_WAIT 状态下重用地址和端口。不设置此选项时,如果服务器异常退出后立即重启,通常会遇到 "Address already in use" 错误,需等待约 2~4 分钟(TIME_WAIT 超时)才能重新绑定。设置此选项可以立即重启服务器,对开发和调试极为有用。

# 设置 SO_REUSEADDR sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.bind(('0.0.0.0', 8080)) sock.listen()

8.2 TCP_NODELAY -- 禁用Nagle算法

Nagle算法通过延迟小数据包发送来减少网络拥塞,但在实时通信场景(如在线游戏、即时通讯)中会导致明显的延迟。TCP_NODELAY 禁用Nagle算法,使小数据包立即发送。注意此选项只能用于TCP套接字。

# 为低延迟应用禁用Nagle算法 sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)

8.3 SO_LINGER -- 控制关闭行为

SO_LINGER 控制 close() 的行为:是否等待未发送数据发送完成后再关闭连接,以及等待的时间。设置 l_onoff=1, l_linger=0 时 close 立即关闭并发送 RST 包而非正常的 FIN 四次挥手,可用于快速释放连接但可能导致数据丢失。

# 设置 SO_LINGER:关闭后等待5秒完成数据发送 sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 5)) # 强制立即关闭(发送RST) sock.setsockopt(socket.SOL_SOCKET, socket.SO_LINGER, struct.pack('ii', 1, 0))

推荐配置:TCP服务器建议始终设置 SO_REUSEADDR 以确保快速重启;低延迟应用设置 TCP_NODELAY;不需要设置 SO_LINGER 时保持默认即可。

九、selectors模块——IO多路复用

9.1 IO多路复用概述

IO多路复用是一种单线程同时监视多个文件描述符(包括socket)的机制。操作系统提供 select、poll、epoll(Linux)、kqueue(BSD/macOS)等系统调用。Python 的 selectors 模块(Python 3.4+)提供了统一的抽象层,自动选择当前平台最高效的实现(如 Linux 上优先使用 epoll)。

9.2 使用selectors实现并发服务器

相比多线程方案,使用 selectors 的事件驱动模型在大量并发连接(数千到数万)时性能更好,因为线程上下文切换和内存开销远小于线程方案。

# selectors_echo_server.py - 基于selectors的事件驱动TCP服务器 import selectors import socket sel = selectors.DefaultSelector() # 自动选择最佳实现 def accept(sock, mask): """处理新连接""" conn, addr = sock.accept() conn.setblocking(False) # selectors要求非阻塞 sel.register(conn, selectors.EVENT_READ, read) print(f'新连接:{addr}') def read(conn, mask): """处理已连接socket的可读事件""" data = conn.recv(1024) if data: conn.sendall(b'[SELECTOR] ' + data) else: # 连接关闭,取消注册并关闭socket print(f'关闭连接:{conn.getpeername()}') sel.unregister(conn) conn.close() # 创建服务器socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('127.0.0.1', 8888)) server.listen(100) server.setblocking(False) sel.register(server, selectors.EVENT_READ, accept) print('selectors事件驱动服务器已启动,监听 127.0.0.1:8888') try: while True: events = sel.select() # 阻塞等待事件发生 for key, mask in events: callback = key.data # 注册时传入的回调函数 callback(key.fileobj, mask) except KeyboardInterrupt: print('服务器关闭') finally: sel.close()

selectors vs 多线程:当并发连接数较少(<1000)且每个连接处理逻辑较重(如涉及大量计算)时,多线程方案更简单直接;当并发连接数高(>5000)且每个连接处理逻辑轻量时,selectors事件驱动模型的内存和性能优势更加明显。现代Python异步框架(asyncio)的底层也广泛使用了selectors或类似的IO多路复用机制。

9.3 selectors API对比

实现类底层系统调用平台并发上限特点
SelectSelectorselect()全平台FD_SETSIZE 限制(通常1024)最兼容,性能最差
PollSelectorpoll()Unix无硬限制比select好,但仍是O(n)扫描
EpollSelectorepoll()Linux 2.5.44+取决于系统内存O(1)事件通知,性能最好
KqueueSelectorkqueue()BSD/macOS取决于系统类epoll,FreeBSD/macOS首选
DefaultSelector自动选择跨平台最优选择生产环境推荐

十、简单HTTP服务器实现

10.1 基于socket的迷你HTTP服务器

理解了socket编程后,我们可以从零实现一个简单的HTTP服务器。HTTP本质上就是基于TCP的文本协议:客户端发送HTTP请求(包含方法、路径、头部),服务器解析请求后回复HTTP响应(状态行、头部、正文)。

# simple_http_server.py - 基于socket的迷你HTTP服务器 import socket import datetime HTTP_RESPONSE = '''\ HTTP/1.1 200 OK\r Content-Type: text/html; charset=utf-8\r Connection: close\r \r <!DOCTYPE html> <html> <head><title>Python Socket HTTP Server</title></head> <body> <h1>Hello from Python Socket!</h1> <p>Server Time: {time}</p> <p>This page was served using raw Python sockets.</p> <ul> <li><a href="/">Home</a></li> <li><a href="/about">About</a></li> </ul> </body> </html>''' def handle_request(conn, addr): """解析HTTP请求并返回响应""" data = conn.recv(4096) if not data: return # 解析请求行:GET /path HTTP/1.1 request_text = data.decode('utf-8', errors='replace') request_line = request_text.split('\r\n')[0] method, path, version = request_line.split(' ') print(f'[{datetime.datetime.now()}] {addr[0]} - {method} {path}') # 根据不同路径返回不同内容 if path == '/': body = '<h1>Welcome to Home</h1><p>路径: /</p>' elif path == '/about': body = '<h1>About</h1><p>这是一个使用Python socket从零实现的HTTP服务器。</p>' else: body = '<h1>404 Not Found</h1><p>请访问 / 或 /about</p>' response = HTTP_RESPONSE.format(time=datetime.datetime.now()) conn.sendall(response.encode('utf-8')) conn.close() # 启动HTTP服务器 server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) server.bind(('127.0.0.1', 8080)) server.listen(10) print('HTTP服务器已启动,访问 http://127.0.0.1:8080') try: while True: conn, addr = server.accept() handle_request(conn, addr) except KeyboardInterrupt: print('服务器关闭') finally: server.close()

运行与测试

将此代码保存为文件并在终端运行。打开浏览器访问 http://127.0.0.1:8080,即可看到服务器返回的HTML页面。服务器的控制台会打印每个请求的日志。这个简单的例子展示了HTTP协议的本质——它就是基于TCP的文本协议,任何语言只要支持socket都可以实现HTTP服务器。

十一、核心要点总结

十二、进一步思考与实践

进阶练习

  1. 文件传输工具:基于TCP实现一个支持大文件断点续传的文件服务器,要求使用固定大小的缓冲区循环读写。
  2. HTTP框架实现:在selectors服务器基础上扩展,添加路由分发、中间件支持等特性,理解Flask/Django底层的工作机制。
  3. WebSocket握手:研究WebSocket协议的HTTP Upgrade握手过程,实现一个简单的WebSocket聊天服务器。
  4. 代理服务器:实现HTTP正向代理和反向代理,理解代理转发的核心逻辑。
  5. 并发模型对比:对同一echo服务分别实现多线程、selectors、asyncio三个版本,在相同条件下压测对比QPS和内存占用。

学习建议:socket网络编程是通向高级Python开发的必经之路。深入理解socket API和底层TCP状态转换,对后续学习asyncio、Twisted、Tornado等异步框架大有裨益。建议在虚拟机或容器中多做实验,使用Wireshark抓包分析TCP的三次握手和四次挥手过程,将理论知识与实践观察结合起来。

扩展阅读:Python官方文档 socket 模块、selectors 模块;《Unix网络编程》卷一(Stevens);《Python网络编程》(Brandon Rhodes);Real Python网站上关于socket编程的系列教程。