Python跨WiFi设备socket通信时while循环recvall接收不全问题
问题根因
- 客户端的接收终止逻辑存在严重缺陷:你用
len(receive_data_part) < 1024作为传输结束的判断依据,完全不符合TCP协议的流特性。TCP是无边界的字节流,单次recv返回的字节数仅取决于当前系统内核缓冲区的可用数据量,和发送端是否完成数据发送没有关联。
跨WiFi传输时,受网络MTU(以太网默认MTU为1500,去掉TCP/IP头部后单次最大传输1460字节有效载荷)、传输延迟、滑动窗口的影响,会出现中间某次recv拿到的数据不足1024字节,但后续还有未到达的数据,此时你提前break就会导致数据截断。本地回环接口传输速率极高,所有数据会一次性进入缓冲区,只有最后一次recv才会返回小于1024字节,所以刚好能正常运行。 - 服务端发送完数据后未主动关闭连接:只有当发送端关闭TCP连接的写通道时,接收端的
recv才会返回0长度的空字节,代表数据传输完成。你当前服务端发送完文件内容后仍保持连接打开,客户端永远无法通过标准方式判断传输结束。
修复方案
方案1:简单修复(服务端发完即关连接,客户端判断空返回终止)
这种方案改动最小,适合单文件传输的简单场景。
修正后服务端代码
import socket server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(("", 5000)) server.listen(5) def send_file(client): read_string = open("textfile", "rb").read() #6KB大小的文本文件 client.sendall(read_string) # 改用sendall保证所有数据被送入发送队列 client.close() # 发送完成后主动关闭连接,通知客户端传输结束 while True: client, data = server.accept() connect_data = client.recv(1024) if connect_data == b"send_string": send_file(client) else: client.close()
修正后客户端代码
import socket client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(("192.168.1.10", 5000)) client.send(b"send_string") receive_data = b"" # 先接收字节流,避免多字节字符截断导致decode错误 while True: # 修正后的recvall循环 receive_data_part = client.recv(1024) if not receive_data_part: # 只有收到空字节代表连接关闭,传输结束 break receive_data += receive_data_part print(receive_data.decode())
方案2:规范实现(先传文件长度,客户端按长度接收)
这种方案适合多命令、多文件传输的复杂场景,不需要每次传输都关闭连接。
核心逻辑是服务端发送文件前,先将文件大小转为4字节的固定长度整数发送,客户端先读取4字节拿到总长度,再循环接收直到达到总长度即可。
修正后服务端关键代码
import struct def send_file(client): read_string = open("textfile", "rb").read() # 先发送4字节的文件长度,大端序 client.sendall(struct.pack('!I', len(read_string))) client.sendall(read_string)
修正后客户端关键代码
import struct client.send(b"send_string") # 先读取4字节拿到总长度 total_len = struct.unpack('!I', client.recv(4))[0] receive_data = b"" while len(receive_data) < total_len: receive_data += client.recv(1024) print(receive_data.decode())
内容的提问来源于stack exchange,提问作者Sh23
相关产品推荐
相关产品推荐

