Python Socket客户端如何连续向服务器发送数据?
最优解决方案:复用单TCP连接+自定义消息边界
TCP是流式协议,本身没有天然的消息边界,连续发送的数据会被合并成字节流。要在单个连接上发送多条数据,核心是让两端能准确区分每条消息的开始和结束,以下是两种高效可靠的实现方案:
方案1:换行符作为消息分隔符(适合文本类消息)
实现简单,通过在每条消息末尾添加换行符\n作为边界,两端以此分割消息。
修改后的服务器代码
import socket IP = '' PORT = 5000 SIZE = 1024 ADDR = (IP, PORT) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: server_socket.bind(ADDR) server_socket.listen() print("服务器已启动") while True: client_socket, client_addr = server_socket.accept() print(f"客户端 {client_addr} 已连接") # 循环接收客户端的多条消息,直到连接关闭 buffer = b'' with client_socket: while True: data = client_socket.recv(SIZE) if not data: print(f"客户端 {client_addr} 已断开") break buffer += data # 按换行符分割消息 while b'\n' in buffer: msg, buffer = buffer.split(b'\n', 1) msg = msg.decode().strip() print(f"收到消息: {msg}") # 返回有业务意义的应答,无需无意义字符串 client_socket.sendall(f"已收到: {msg}".encode() + b'\n')
修改后的客户端代码
import socket username = 'test' userpass = '1234' SERVER_IP = 'localhost' SERVER_PORT = 5000 SIZE = 1024 SERVER_ADDR = (SERVER_IP, SERVER_PORT) # 复用单个Socket连接发送多条消息 with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket: client_socket.connect(SERVER_ADDR) # 发送用户名,末尾加换行符标识边界 client_socket.sendall(f"{username}\n".encode()) resp = client_socket.recv(SIZE).decode().strip() print(f"服务器应答: {resp}") # 发送密码,同样添加换行符 client_socket.sendall(f"{userpass}\n".encode()) resp = client_socket.recv(SIZE).decode().strip() print(f"服务器应答: {resp}") # 可继续发送更多消息...
方案2:长度前缀法(更可靠,支持二进制/含特殊字符的消息)
如果消息包含换行符或为二进制数据,推荐先发送消息的字节长度(固定4字节整数),再发送消息内容,从根本上避免粘包问题。
修改后的服务器代码
import socket import struct IP = '' PORT = 5000 SIZE = 1024 ADDR = (IP, PORT) # 定义长度前缀格式:4字节大端无符号整数 LEN_FORMAT = '!I' LEN_SIZE = struct.calcsize(LEN_FORMAT) with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as server_socket: server_socket.bind(ADDR) server_socket.listen() print("服务器已启动") while True: client_socket, client_addr = server_socket.accept() print(f"客户端 {client_addr} 已连接") with client_socket: while True: # 先接收消息长度前缀 len_data = client_socket.recv(LEN_SIZE) if not len_data: print(f"客户端 {client_addr} 已断开") break msg_len = struct.unpack(LEN_FORMAT, len_data)[0] # 接收对应长度的消息内容 msg_data = b'' while len(msg_data) < msg_len: chunk = client_socket.recv(min(SIZE, msg_len - len(msg_data))) if not chunk: break msg_data += chunk if len(msg_data) != msg_len: print("消息不完整,连接中断") break msg = msg_data.decode() print(f"收到消息: {msg}") # 返回带长度前缀的应答 resp = f"已收到: {msg}".encode() client_socket.sendall(struct.pack(LEN_FORMAT, len(resp)) + resp)
修改后的客户端代码
import socket import struct username = 'test' userpass = '1234' SERVER_IP = 'localhost' SERVER_PORT = 5000 SIZE = 1024 SERVER_ADDR = (SERVER_IP, SERVER_PORT) LEN_FORMAT = '!I' LEN_SIZE = struct.calcsize(LEN_FORMAT) def send_msg(sock, msg): msg_bytes = msg.encode() # 先发送消息长度 sock.sendall(struct.pack(LEN_FORMAT, len(msg_bytes))) # 再发送消息内容 sock.sendall(msg_bytes) def recv_msg(sock): # 接收长度前缀 len_data = sock.recv(LEN_SIZE) if not len_data: return None msg_len = struct.unpack(LEN_FORMAT, len_data)[0] # 接收消息内容 msg_data = b'' while len(msg_data) < msg_len: chunk = sock.recv(min(SIZE, msg_len - len(msg_data))) if not chunk: return None msg_data += chunk return msg_data.decode() with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as client_socket: client_socket.connect(SERVER_ADDR) # 发送用户名 send_msg(client_socket, username) resp = recv_msg(client_socket) print(f"服务器应答: {resp}") # 发送密码 send_msg(client_socket, userpass) resp = recv_msg(client_socket) print(f"服务器应答: {resp}") # 可继续发送其他消息...
方案优势
- 复用单个TCP连接,避免频繁创建/关闭连接的握手、挥手开销
- 无需发送无意义应答,服务器可返回业务相关的有效响应
- 清晰的消息边界,彻底解决TCP流式传输的粘包问题
内容的提问来源于stack exchange,提问作者wookidookik123
相关产品推荐
相关产品推荐

