选 readexactly() 当协议要求固定长度头,read() 用于允许短读的流式转发,readuntil() 适合有分隔符的文本协议;StreamReader 无 readinto() 因其自动管理缓冲区,内存问题多因未 await 协程或未关闭 transport。

asyncio.StreamReader.read() 和 readexactly() 怎么选
读二进制流时,read() 返回最多 n 字节,可能少于请求量;readexactly() 则严格等待足额字节或抛 IncompleteReadError。选哪个取决于协议是否允许“短读”。
常见错误现象:用 read(1024) 期望拿到完整消息头,结果只读到 3 字节就返回,后续解析直接崩溃。
- HTTP/RTSP 等文本协议头部通常有明确分隔(如
\r\n\r\n),优先用readuntil(b'\r\n\r\n') - 自定义二进制协议若含固定长度头(如前 4 字节是 payload 长度),先
readexactly(4)解出长度,再readexactly(n) - 单纯转发流(如代理、日志抓取)可用
read(8192)配合循环,但需检查返回值是否为b''(对端关闭)
为什么 readinto() 不支持 asyncio.StreamReader
StreamReader 没有 readinto() 方法——它不接受预分配的 bytearray 缓冲区。这是设计使然:底层 asyncio 的缓冲管理是自动的,避免用户误操作破坏内部状态。
想复用缓冲区?不行。强行模拟会掉坑:memoryview(buf)[:n] = await reader.read(n) 看似省内存,实则多一次拷贝,且 reader.read() 返回新 bytes 对象,无法绕过分配。
- 真正需要零拷贝场景(如高频图像帧传输),应换用
uvloop+socket.recv_into()原生接口,绕过StreamReader - 普通服务中,
read()返回的bytes生命周期由 Python GC 管理,只要不长期持有引用,压力可控 - 若发现内存持续上涨,大概率是忘了
await某个协程,导致StreamReader缓冲区越积越多
处理粘包和半包的实际写法
网络层不保证消息边界,StreamReader 只管按需吐字节。所谓“粘包”本质是应用层没定义好协议边界,不是 StreamReader 的 bug。
典型错误:收到数据后直接 json.loads(),结果因半包报 JSONDecodeError: Expecting value。
- 文本协议:用
readuntil(b'\n')或readuntil(b'\r\n')拆行,比手动找\n安全(避免阻塞在中间) - 二进制协议:必须先读定长头,再读变长体。例如头结构为
struct.pack('!I', length),则header = await reader.readexactly(4); length = int.from_bytes(header, 'big') - 超时控制不能只靠
asyncio.wait_for()包一层——它只管协程执行时间,不中断底层 socket 等待。要设reader.set_exception_on_timeout(True)并捕获asyncio.TimeoutError
关闭流时容易漏掉的清理动作
StreamReader 本身没有 close() 方法,它的生命周期绑定在底层 transport 上。很多人调用完 reader.read() 就以为完事了,其实没释放资源。
常见错误现象:程序跑几小时后报 OSError: [Errno 24] Too many open files,查下来是成百上千个 StreamReader 对应的 socket 还开着。
- 务必确保 transport 被关闭:如果用
asyncio.open_connection(),要writer.close(); await writer.wait_closed() - 若用
asyncio.start_server(),客户端断开后 handler 函数退出即自动清理;但 handler 内部若启了子任务(如后台心跳),必须显式asyncio.create_task()并在连接关闭时cancel() - 调试时可打印
len(asyncio.all_tasks())和len(asyncio.get_event_loop()._transports)快速定位泄漏点
二进制流的边界永远在协议里,不在 StreamReader 的方法签名里。盯着文档看参数不如打开 Wireshark 抓一包,确认你期待的字节真的在网络上传过去了。
Python免费学习笔记(深入):立即使用
在学习笔记中,你将探索 Python 的核心概念和高级技巧!











