Python Socket 套接字编程从客户端到多线程服务器实战
本文详解 Python Socket 套接字编程的核心流程。通过逐步演示如何创建 TCP 连接、发送与接收数据,并对比单线程与多线程服务器的实现差异,帮助开发者快速掌握基于 Python 的网络通信基础,解决实际开发中的客户端交互与并发处理问题。
Socket 编程的目标与基础概念
Socket(套接字)是操作系统提供的网络通信接口,任何跨设备的报文交换都依赖它。浏览器访问网页、即时通讯软件收发消息,底层均走 Socket 通道。Python 内置的 socket 模块提供了与 C 语言高度一致的 API,主要支持两种传输协议:
- TCP(SOCK_STREAM):面向连接、可靠传输、按序到达,适合 HTTP 请求、文件传输等场景。
- UDP(SOCK_DGRAM):无连接、不可靠但低延迟,适合音视频流或高频轮询。本文以 TCP 为主进行讲解。
准备条件与运行环境
编写 Socket 程序无需安装第三方包,依赖 Python 标准库。请确保运行环境为 Python 3.6 及以上版本。以下为通用前置准备:
- 打开系统命令行终端(Windows PowerShell / macOS Terminal / Linux Bash)。
- 新建两个空白文本文件,分别命名为
client.py与server.py。 - 准备本地测试工具(如
telnet或nc),用于模拟外部客户端接入。
第1步:构建 TCP 客户端连接
第1步:创建 Socket 实例并配置参数
目的:初始化网络通信管道。
入口:Python 标准库 socket。
动作:调用 socket.socket() 构造函数。
参数:socket.AF_INET(IPv4地址族)与 socket.SOCK_STREAM(TCP协议)。
完成状态:控制台无报错并返回 Socket 描述符对象,变量名记为 s。
import socket
import sys
try:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Socket Created")
except socket.error as msg:
print(f"Failed to create socket. Error code: {msg[0]}, Error message: {msg[1]}")
sys.exit()
第2步:解析域名并完成连接
目的:获取目标主机 IP 并建立握手。
入口:上一步创建的 Socket 对象 s 与 socket.gethostbyname()。
动作:将域名解析为 IPv4 字符串,随后调用 s.connect() 发起三次握手。
参数:远程主机地址(如 "127.0.0.1" 或公网域名)与端口号(如 80)。
完成状态:终端打印连接成功日志,阻塞状态解除表示握手就绪。
host = "127.0.0.1" # 实际测试可替换为任意可访问域名
port = 8888
try:
remote_ip = socket.gethostbyname(host)
except socket.gaierror:
print("Hostname could not be resolved. Exiting")
sys.exit()
s.connect((remote_ip, port))
print(f"Socket Connected to {host} on ip {remote_ip}")
第3步:发送请求并读取响应数据
目的:按应用层协议投递报文并获取反馈。
入口:已连接的 Socket 对象 s。
动作:构造符合目标的报文头,调用 s.sendall() 全量发送;调用 s.recv(4096) 接收缓冲区数据。
参数:HTTP GET 请求字符串需携带 \r\n 结束符;接收长度可根据业务调整。
完成状态:控制台输出服务器返回的原始字节流或解析后的文本内容。
message = b"GET / HTTP/1.1\r\nHost: " + host.encode() + b"\r\n\r\n"
try:
s.sendall(message)
print("Message send successfully")
except socket.error:
print("Send failed")
sys.exit()
reply = s.recv(4096)
print(reply.decode("utf-8", errors="ignore"))
s.close()
完成判断:终端正常打印服务器响应的 HTTP 状态码与头部字段(如 HTTP/1.1 200 OK 或重定向信息),即为客户端链路打通。
第2步:搭建基础服务器端
第1步:绑定地址与监听端口
目的:预留系统资源并接受外部请求路由。
入口:新建的 Socket 对象 s。
动作:绑定本机所有可用接口(空字符串)与指定端口,切换至监听模式。
参数:HOST = ""(代表 0.0.0.0),PORT = 8888(避开系统保留端口);listen(10) 设置等待队列阈值。
完成状态:调用 s.listen() 后控制台打印监听状态,此时进程进入等待态。
HOST = ""
PORT = 8888
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print("Socket created")
try:
s.bind((HOST, PORT))
except socket.error as msg:
print(f"Bind failed. Error Code: {msg[0]} Message: {msg[1]}")
sys.exit()
s.listen(10)
print("Socket now listening")
第2步:接受连接并处理单次交互
目的:捕获握手完成的客户端会话并回传数据。
入口:监听中的主 Socket s。
动作:调用阻塞方法 s.accept() 提取客户端对象 conn 与元组地址 addr;随后执行读写闭环。
参数:recv(1024) 限制单次最大缓冲,sendall() 保证粘包拆分。
完成状态:打印客户端 IP 与随机高位端口号,收到数据后原路返回拼接回复,最后调用 close() 释放会话。
while True:
conn, addr = s.accept()
print(f"Connected with {addr[0]}:{str(addr[1])}")
data = conn.recv(1024)
reply = b"OK..." + data
if not data:
break
conn.sendall(reply)
conn.close()
第3步:升级为多线程并发服务器
上述循环结构采用串行处理,主线程在 accept 处被占用时会阻塞新接入请求。生产环境通常采用线程池或独立线程模型隔离业务逻辑。
第1步:封装独立线程处理函数
目的:将长连接读写逻辑剥离出主调度循环。
入口:threading 模块与连接描述符 conn。
动作:定义回调函数负责死循环收发消息,异常断开时安全退出子线程。
参数:固定接收窗口 1024,心跳检测可在此扩展。
完成状态:函数内部不抛出未捕获异常,子线程自动随连接关闭而终止。
import threading
def clientthread(conn):
conn.send(b"Welcome to the server. Type something and hit enter\n")
while True:
data = conn.recv(1024)
reply = b"OK..." + data
if not data:
break
conn.sendall(reply)
conn.close()
第2步:主循环启动新线程维持服务
目的:实现高并发接入而不阻塞排队。
入口:主 Socket s 与 clientthread。
动作:每次 accept() 成功后,实例化 threading.Thread 并传入目标函数与参数元组,调用 .start() 立即派发。
参数:daemon=False 默认保持主进程退出时强制清理(视业务需求调整)。
完成状态:多终端同时 telnet 127.0.0.1 8888 均可独立收发,主进程持续运行且 CPU 占用平稳。
while True:
conn, addr = s.accept()
print(f"New connection: {addr[0]}:{addr[1]}")
thread = threading.Thread(target=clientthread, args=(conn,))
thread.start()
常见运行异常排查
开发过程中最常遇到的错误是 OSError: [Errno 98] Address already in use。该现象通常由以下原因引发:
- 端口残留:上次程序非正常退出(如 Ctrl+C 强杀),内核 TIME_WAIT 状态尚未清除。等待 30~120 秒即可自愈,或更换
PORT值绕过。 - 权限冲突:Linux/macOS 下尝试绑定
1024以下特权端口会触发拒绝访问。请改用高位端口或提权运行。 - IP 复用检查:若需在重启时立即释放端口,可在 bind 前加入
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)。
以上就是 Python Socket 编程从客户端到多线程服务器的详细内容,更多关于 Python 网络开发的基础资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













