Python Socket传输MySQL导出字符串时丢失末尾字符故障排查问询
问题原因与修复方案
根因分析
故障核心是混淆了UTF-8字符串的字符长度与编码后的字节长度:
- 普通ASCII字符的UTF-8编码长度与字符长度一致,所以常规消息传输正常
- MySQL导出的字符串包含
Ł这类非ASCII拉丁扩展字符,单个字符UTF-8编码占2字节,你客户端代码中len(msg_to_send)计算的是字符个数,远小于实际编码后的字节总长度 - 你将错误的长度值放在消息头传给服务端,服务端按偏小的长度收消息,就会少收末尾的几个字节,末尾的
]就会留在缓冲区,被下一次读取消息头的操作拿到,就出现了]6无法转整数的报错。
另外原代码还存在socket半包问题:send不保证一次性发送完所有字节,recv也不保证一次性读满指定长度的字节,就算长度计算正确,传输大内容时也可能出现异常。
修复代码
客户端修改
import socket import time PORT = 9696 FORMAT = 'utf-8' SERVER = "localhost" ADDR = (SERVER, PORT) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client.connect(ADDR) def send(msg): msg_bytes = msg.encode(FORMAT) # 计算编码后的字节长度作为头部值 msg_len = len(msg_bytes) header = str(msg_len) + ("#" * (30 - len(str(msg_len)))) header_bytes = header.encode(FORMAT) # 循环发送保证头部全部发出 sent = 0 while sent < len(header_bytes): sent += client.send(header_bytes[sent:]) # 循环发送保证消息内容全部发出 sent = 0 while sent < len(msg_bytes): sent += client.send(msg_bytes[sent:]) # 替换为你的MySQL导出字符串 msg_to_send = "[('index', 'bigint(20)', 'YES', 'MUL', None, ''), ('LP', 'text', 'YES', '', None, ''), (' NR-a', 'text', 'YES', '', None, ''), ('LITERA-a', 'text', 'YES', '', None, ''), ('MUFA-a', 'double', 'YES', '', None, ''), ('UWAGI-a', 'text', 'YES', '', None, ''), ('TUBY-a', 'double', 'YES', '', None, ''), ('DATA1-a', 'text', 'YES', '', None, ''), ('DATA2-a', 'text', 'YES', '', None, ''), ('SPAWY-i', 'text', 'YES', '', None, ''), ('SPAWY_DAC-i', 'text', 'YES', '', None, ''), ('LOG-a', 'text', 'YES', '', None, ''), ('SPLIT1X4-i', 'text', 'YES', '', None, ''), ('SPLIT1X8-i', 'text', 'YES', '', None, ''), ('SPLIT1X16-i', 'text', 'YES', '', None, ''), ('ADAPTERY SIMPLE-i', 'double', 'YES', '', None, ''), ('ADAPTERY DUPLEX-i', 'double', 'YES', '', None, ''), ('PIGTAIL-i', 'double', 'YES', '', None, ''), ('OLT-a', 'double', 'YES', '', None, ''), ('NEXT-a', 'double', 'YES', '', None, ''), ('ODNOGA_1-a', 'double', 'YES', '', None, ''), ('ODNOGA_2-a', 'double', 'YES', '', None, ''), ('ODNOGA_3-a', 'double', 'YES', '', None, ''), ('ODNOGA_4-a', 'double', 'YES', '', None, ''), ('DROP30-i', 'double', 'YES', '', None, ''), ('DROP50-i', 'double', 'YES', '', None, ''), ('DROP70-i', 'double', 'YES', '', None, ''), ('DROP80-i', 'double', 'YES', '', None, ''), ('DROP100-i', 'double', 'YES', '', None, ''), ('DROP120-i', 'double', 'YES', '', None, ''), ('DROP150-i', 'double', 'YES', '', None, ''), ('DROP200-i', 'double', 'YES', '', None, ''), ('MAŁPKI-i', 'double', 'YES', '', None, '')]" time.sleep(3) for i in range(1000): send(msg_to_send) while True: pass
服务端修改
import socket import threading PORT = 9696 SERVER = "localhost" ADDR = (SERVER, PORT) FORMAT = 'utf-8' server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server.bind(ADDR) # 新增工具函数:保证收满指定长度的字节 def recv_exact(conn, length): data = b'' while len(data) < length: chunk = conn.recv(length - len(data)) if not chunk: raise RuntimeError("连接已断开") data += chunk return data def handle_client(conn): while True: # 先收满30字节的头部 header = recv_exact(conn, 30).decode(FORMAT) actual_size = int(header.replace("#", "")) # 再收满对应长度的消息内容 msg_bytes = recv_exact(conn, actual_size) received_message = msg_bytes.decode(FORMAT) print(len(received_message)) # 验证末尾字符 print(received_message[-1]) def start(): server.listen() while True: conn, addr = server.accept() thread = threading.Thread(target=handle_client, args=(conn,)) thread.start() start()
内容的提问来源于stack exchange,提问作者user16712485
相关产品推荐
相关产品推荐

