Python Socket多数据收发故障:客户端接收串连问题排查与修复
Python Socket多组数据收发串接问题的解决办法
你遇到的这个串连接收问题,本质是TCP Socket的特性导致的——TCP是流式协议,它不会帮你区分不同消息之间的边界。你在服务器端连续调用send()发送的内容,操作系统会通过Nagle算法优化,把多个小数据包合并成一个TCP段发送,所以客户端第一次调用recv(60)时,直接把所有发送的内容都读走了,后面的recv()自然就拿不到数据了。
下面给你两种靠谱的修复方案,你可以根据需求选择:
方案一:用分隔符区分消息边界
这种方法简单直观,适合内容里不会出现分隔符的场景,比如文件名和普通文本数据。
修改服务器代码
给每个要发送的消息末尾加上换行符\n,作为消息结束的标记:
import socket def send(): host = 'localhost' port = 9999 address = (host, port) sock = socket.socket() sock.bind(address) sock.listen(5) print('listining for connection...') con,addr = sock.accept() print('Got connection from',addr) file1name = 'file1.txt' # 给文件名加换行符后发送 con.send((file1name + '\n').encode('utf-8')) file1data = 'this is file 1' con.send((file1data + '\n').encode('utf-8')) file2name = 'file2.txt' con.send((file2name + '\n').encode('utf-8')) file2data = 'this is file 2' con.send((file2data + '\n').encode('utf-8')) con.close() sock.shutdown(1) sock.close() print('connection closed!') send()
修改客户端代码
用makefile()把Socket包装成文件对象,按行读取(自动识别换行符作为边界):
import socket host = 'localhost' port = 9999 address = (host, port) sock = socket.socket() sock.connect(address) print('connected to', address) # 用文件对象按行读取,自动处理换行分隔符 with sock.makefile('r', encoding='utf-8') as f: while True: filename = f.readline().strip() # strip去掉换行符 if not filename: break print('filename -', filename) data = f.readline().strip() print('data -', data) sock.shutdown(1) sock.close()
方案二:先发送数据长度,再发送内容
这种方法更通用可靠,适合任何类型的数据(包括含换行符或其他特殊字符的内容),是工业级应用常用的方式。原理是先告诉对方要接收的数据长度,再发送实际内容,让客户端可以精准读取对应长度的数据。
修改服务器代码
需要导入struct模块来打包数据长度:
import socket import struct def send(): host = 'localhost' port = 9999 address = (host, port) sock = socket.socket() sock.bind(address) sock.listen(5) print('listining for connection...') con,addr = sock.accept() print('Got connection from',addr) # 封装一个发送消息的函数:先发长度,再发内容 def send_msg(msg): msg_bytes = msg.encode('utf-8') # 把消息长度打包成4字节的大端整数(!I表示网络字节序的无符号int) length_packed = struct.pack('!I', len(msg_bytes)) con.send(length_packed) con.send(msg_bytes) file1name = 'file1.txt' send_msg(file1name) file1data = 'this is file 1' send_msg(file1data) file2name = 'file2.txt' send_msg(file2name) file2data = 'this is file 2' send_msg(file2data) con.close() sock.shutdown(1) sock.close() print('connection closed!') send()
修改客户端代码
同样需要导入struct模块,先读取长度,再读取对应长度的内容:
import socket import struct host = 'localhost' port = 9999 address = (host, port) sock = socket.socket() sock.connect(address) print('connected to', address) # 封装一个接收消息的函数:先读长度,再读对应内容 def recv_msg(): # 先接收4字节的长度数据 length_bytes = sock.recv(4) if not length_bytes: # 连接关闭时返回空 return None # 解包得到消息长度 msg_length = struct.unpack('!I', length_bytes)[0] # 接收对应长度的消息内容 return sock.recv(msg_length).decode('utf-8') while True: filename = recv_msg() if not filename: break print('filename -', filename) data = recv_msg() print('data -', data) sock.shutdown(1) sock.close()
这两种方案都能解决你现在的串接问题,方案一简单易实现,方案二更健壮通用,你可以根据自己的实际场景选择~
内容的提问来源于stack exchange,提问作者user12260778




