You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 20:45:34