8.5 KiB
Socket 通信模块技术文档
本项目提供了一个基于 Python 的简单 TCP 客户端/服务器通信框架,支持多线程并发处理客户端连接,并封装了后台任务调用机制。适用于本地测试、轻量级网络服务开发等场景。
目录
依赖说明
import os
import time
import threading
import sys
import socket
所需标准库模块:
socket:用于实现 TCP 网络通信。threading:支持多线程并发处理多个客户端连接。time:用于延时控制和测试。- 其他为通用系统操作支持。
核心功能概览
| 组件 | 功能 |
|---|---|
get_free_local_addr() |
获取当前机器可用的 IP 地址(通过 DNS 请求探测) |
background / BackgroundCall() |
异步执行函数的线程包装器 |
SocketServer |
多线程 TCP 服务器,可接受并异步处理多个客户端连接 |
SocketClient |
TCP 客户端,用于连接服务器、发送和接收数据 |
| 自定义异常 | SocketServerError, SocketClientError 提供清晰错误分类 |
工具函数
get_free_local_addr()
获取本机在联网状态下对外通信所使用的 IP 地址和临时端口。
函数签名
def get_free_local_addr():
返回值
(ip: str, port: int):元组形式返回本机出口 IP 和操作系统分配的临时端口号。
实现原理
通过创建一个 UDP 套接字连接到公共 DNS 服务器 8.8.8.8:80,不实际发送数据,仅用于确定本地绑定地址。
⚠️ 注意:此方法依赖外网可达性;若无网络或防火墙限制可能失败。
示例
ip, port = get_free_local_addr()
print(ip) # 输出如 '192.168.1.100'
后台任务类
class background(threading.Thread)
封装一个可在后台运行的函数调用。
属性
| 属性名 | 类型 | 描述 |
|---|---|---|
func |
callable |
要执行的目标函数 |
kw |
dict |
关键字参数传递给目标函数 |
方法
__init__(self, func, kw)
初始化线程对象。
- 参数:
func: 可调用对象(函数)kw: 字典形式的关键字参数{key: value}
run(self)
重写 Thread.run(),自动调用 self.func(**self.kw)。
BackgroundCall(func, datas)
便捷函数:启动一个后台线程执行指定函数。
参数
func: 待执行函数datas: 传入该函数的关键字参数字典
行为
- 创建
background实例 - 调用
.start()启动线程 - 不阻塞主线程
示例
def echo(data):
print("Received:", data)
BackgroundCall(echo, {'data': 'Hello'})
# 在后台打印 "Received: Hello"
异常定义
class SocketServerError(Exception)
表示服务器端发生的严重错误,例如绑定地址失败、未就绪运行等。
class SocketClientError(Exception)
表示客户端连接或通信过程中出现的问题,如无法连接、读写出错等。
使用自定义异常便于上层捕获特定错误类型进行处理。
SocketServer 类
多线程 TCP 服务器,监听指定地址和端口,每个新连接由独立线程处理。
类定义
class SocketServer(threading.Thread)
继承自 threading.Thread,以非守护模式运行。
构造函数 __init__(host, port, max_connect=10, callee=None)
参数
| 参数 | 类型 | 默认值 | 说明 |
|---|---|---|---|
host |
str |
— | 绑定主机地址,如 'localhost' 或 '0.0.0.0' |
port |
int |
— | 绑定端口号 |
max_connect |
int |
10 |
最大挂起连接数(listen 队列长度) |
callee |
callable |
None |
处理每个客户端连接的回调函数 |
回调函数原型
def handler(conn: socket.socket, addr: tuple):
pass
其中:
conn: 客户端连接套接字addr: 客户端地址(ip, port)
初始化行为
- 设置守护线程为
False - 创建并尝试绑定监听套接字
- 若成功则
ready = True,否则记录日志但继续构造
方法
setSocketServer()
内部方法:创建并配置服务器套接字。
- 创建 TCP 套接字 (
AF_INET,SOCK_STREAM) - 绑定
(host, port) - 开始监听(最大连接队列长度为
max_c) - 成功后设置
self.ready = True
若失败会打印错误信息但不会抛出异常(构造阶段容错)
run()
线程入口函数,循环接受客户端连接。
- 检查是否
ready,否则抛出SocketServerError - 进入无限循环等待客户端接入
- 每次接受连接后,使用
BackgroundCall将callee函数异步执行
单个连接处理完全解耦,不影响主服务循环
stop()
请求停止服务器运行。
- 设置标志位
keep_running = 0 - 下一次循环将退出
run()函数 - ⚠️ 当前已建立的连接不会被主动关闭
callee(self, conn, addr)(默认回显处理)
内置默认处理函数:持续接收数据并原样返回(回显服务),直到连接断开。
❗ 存在一个拼写错误:
con.close()应为conn.close()
修正建议
def callee(self, conn, addr):
try:
while True:
d = conn.recv(1024)
if not d: # 接收到空数据表示连接关闭
break
conn.send(d)
finally:
conn.close() # 正确关闭连接
SocketClient 类
TCP 客户端封装,提供连接管理及基本 I/O 操作。
构造函数 __init__(host, port)
自动尝试连接指定服务器。
参数
host: 服务器地址port: 端口
行为
- 创建 TCP 套接字
- 调用
connect()方法连接服务器 - 成功则
ready=True,失败则抛出SocketClientError
方法
timeout(tim)
设置套接字阻塞模式和超时时间。
tim == 0: 非阻塞模式tim > 0: 阻塞模式 + 超时秒数
内部调用
setblocking()和settimeout()
connect()
重新建立与服务器的连接。
- 若失败打印错误信息并抛出
SocketClientError
read(size)
从服务器读取最多 size 字节数据。
- 成功返回字节串(
bytes) - 失败打印错误并抛出
SocketClientError
write(data)
向服务器发送数据。
data必须是字节串(bytes),若传入字符串需先编码- 错误时抛出异常
close()
关闭连接,释放资源。
- 调用
sock.close() - 设置
ready = False
主程序示例
if __name__ == '__main__':
s = SocketServer('localhost', 12232)
s.start()
time.sleep(5) # 等待服务器启动
while True:
c = SocketClient('localhost', 12232)
msg = 'msg1'
print("send:", msg)
c.write(msg.encode()) # 注意:必须 encode 成 bytes
d = c.read(1024)
print("get:", d.decode()) # 解码为字符串
c.close()
time.sleep(1)
✅ 改进建议:添加异常处理防止客户端崩溃中断循环
使用建议与注意事项
✅ 推荐实践
- 确保数据编码一致性
c.write(b'msg') # 字节串 c.write('msg'.encode()) # 字符串转字节 - 捕获异常避免中断
try: c = SocketClient(...) except SocketClientError: time.sleep(1) continue - 合理设置超时
c.timeout(5) # 5秒超时防卡死
⚠️ 已知问题
| 问题 | 描述 | 建议修复 |
|---|---|---|
con.close() 拼写错误 |
导致连接未正确关闭 | 改为 conn.close() |
callee 默认函数无异常处理 |
可能导致线程异常退出 | 包裹 try...finally |
| 构造中静默忽略异常 | 难以调试绑定失败原因 | 抛出或记录详细日志 |
write() 中错误提示写成了 'recv error' |
日志误导 | 改为 'send error' |
🔧 可扩展方向
- 添加 SSL/TLS 支持
- 支持 IPv6
- 增加连接池或心跳检测
- 提供异步 IO 版本(asyncio)
许可证
本代码为示例用途,遵循 MIT License(除非另有声明)。
文档版本:v1.0
更新日期:2025年4月5日