如何使用Python socket实现服务端发多列表、客户端分别接收
实现socket服务端多列表分开收发的方案
核心问题:TCP是面向流的传输协议,没有天然的消息边界,如果直接连续调用send发送3个序列化后的列表,客户端recv时可能一次收到多个序列化数据的拼接内容,也可能分多次收到单个列表的分片数据,无法准确拆分出独立的列表。
下面提供两种可直接运行的实现方案:
方案1:打包一次性传输(改动最小,适合固定发3个列表的场景)
直接把3个列表打包为一个元组序列化后一次性发送,客户端反序列化后直接拆分即可,无需额外处理粘包问题。
改后server.py代码
import socket import pickle am0=['AQ-20', 'A3000', 'AQ-26', 'A5000', 'AQ-33', 'A5000pro', 'AQ-33pro'] am1=['A10000Pro', 'AQ-43', 'AX-48', 'AX-58', 'AX-68'] am2=['Material', 'nan', 'Steel', 'Stainless S.', 'Stainless S. 1.403'] # 三个列表打包成元组后序列化 all_data = pickle.dumps((am0, am1, am2)) mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) # 注意:1024以下是特权端口需要管理员权限,改为12345避免报错 mysocket.bind(('127.0.0.1', 12345)) buffer_size = 1024 mysocket.listen() (client, (ip,port)) = mysocket.accept() client.send(all_data) mysocket.close()
改后client.py代码
import socket import pickle host = '127.0.0.1' port = 12345 buffer_size = 1024 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) # 一次性接收后反序列化得到三个列表 data = pickle.loads(s.recv(buffer_size)) am0, am1, am2 = data print(am0) print(am1) print(am2) s.close()
方案2:带长度前缀分开发送(灵活可扩展,适合后续增减发送的列表数量)
每个列表序列化后,先发送4字节的长度标识(用struct模块打包),再发送实际的序列化数据,客户端每次先读取长度,再接收对应长度的字节,即可准确拆分每个独立列表。
改后server.py代码
import socket import pickle import struct am0=['AQ-20', 'A3000', 'AQ-26', 'A5000', 'AQ-33', 'A5000pro', 'AQ-33pro'] am1=['A10000Pro', 'AQ-43', 'AX-48', 'AX-58', 'AX-68'] am2=['Material', 'nan', 'Steel', 'Stainless S.', 'Stainless S. 1.403'] # 序列化每个列表 serial_list = [pickle.dumps(am0), pickle.dumps(am1), pickle.dumps(am2)] mysocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) mysocket.bind(('127.0.0.1', 12345)) buffer_size = 1024 mysocket.listen() (client, (ip,port)) = mysocket.accept() # 循环发送每个列表:先发4字节长度,再发实际数据 for item in serial_list: # !I 表示网络字节序的无符号整型,占4字节 client.send(struct.pack('!I', len(item))) client.send(item) mysocket.close()
改后client.py代码
import socket import pickle import struct host = '127.0.0.1' port = 12345 buffer_size = 1024 s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) s.connect((host, port)) # 封装读取指定长度字节的函数,避免分片导致数据不全 def recv_exact(n): data = b'' while len(data) < n: packet = s.recv(n - len(data)) if not packet: return None data += packet return data am_list = [] # 循环接收3次,对应三个列表 for _ in range(3): # 先收4字节解出数据长度 len_bytes = recv_exact(4) data_len = struct.unpack('!I', len_bytes)[0] # 收对应长度的字节反序列化 item_data = recv_exact(data_len) am_list.append(pickle.loads(item_data)) am0, am1, am2 = am_list print(am0) print(am1) print(am2) s.close()
注意事项
- 避免使用1024以下的端口,这类端口属于系统特权端口,需要管理员/root权限才能绑定,建议使用1024~65535之间的未占用端口
- 如果要发送的列表数据量很大,超过设置的
buffer_size,必须用recv_exact这类循环接收的逻辑,保证数据收全
内容的提问来源于stack exchange,提问作者Batuhan Karakaya
相关产品推荐
相关产品推荐

