Python Socket编程中JSON发送字节时服务器JSON解码报错问题
问题:Socket传输JSON时服务端解析失败
我编写了一个Socket程序,需求如下:选择文件,读取文件内容,将读取的文件转换为字节列表,每次将列表中的一个元素以JSON格式单独发送到服务器,在服务器端加载JSON并打印。目前文件读取、字节列表转换、JSON格式发送均正常,但服务器端解析JSON时出现如下错误:
{'type': 'fileUpload', 'file': 'iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAAACXBIWXMAAA7EAAAOxAGVKw4bAAATPElEQVR42s1be2xT1R//3Hv7Wtm6dXNjZWxswgabDsabgThIjAMVBKe44BOjRjBEUYnySAQfBBPBVwJBxWCMiqIoBAQHSoLKBBcCDDYeY2wdm92rXdv1dV/f3x+kl7a7bccYv/hNbnp7envu+Z7zPd/v53zO9zB0TQAADMMgeB/8DgChvwe/q92HSrTy0N8i6+np6QHDMOjo6IAkSRg9ejRYlgURQRAE/PPPPxg5ciQyMzP7VX+0d4XpFNS+u7sbAJCWlgYigtfrxZAhQ/r8IbSj1CqO1qDITlVrsCzLAABZlkFE0Gq1yjOSJKGzsxPJyckwGAzqykRRNFZ7NcGHAoEAJElCWlqa8kI15dQqH4hIkgRJkiDLMliWhU6nA8uyUS2N4zhl5COVjmxD6P/UPsN+p9DhiaJYNJONvJckCe3t7ZBlGSaTCQaDARzHQZZldHd3w+12w2q1orq6Gr29vfB4PHC73TCZTHj44YcxdepUsCwLnueRkJCgmP/NTLdY7QcARpZl6q9Jhz6jZg29vb147rnncPjwYZjNZlgsFuTk5MDhcODkyZNwOp0IBAKKqYeKwWDAQw89hKFDh6KmpgZlZWV48sknMWrUqD7vVBmzfvuryGmsiTSRWBKtg2RZhtVqRV1dHXieR3d3N7q7u9HY2IiamhqwLIsFCxZAlmV89913qnX7/X58++23yqg7HA40NTVh+PDhKC8vx6hRo5Camgqj0RjVCgKBAHQ6HQRBgF6vj9pJoR2jiWc28aZGU1MTvvrqK9TV1cFsNkMQBBgMBoiiiFmzZiE/Px88zyMjIwP//POP6ujpdDoYDAbk5eXh6tWrcLlcEAQBL774Ik6ePInXX38dDMNg69atKCkpUVWEYRhoNBowDAOO42JGqzA/Qv0QWZb7lEmSRDU1NTR79myaMmUKVVVV0Z49e+iJJ56glJQUAkAcxxHHcQQg7GIYRrlnWZays7NJr9fTkiVLqKSkhMaMGUNFRUW0cuVKmjZtGqWkpBDDMPTII4/QqVOniOf5G2qzWvuDguCPkX8IXoFAgCKfkWWZqquracGCBfTll1+S1+ul48eP04QJEwgAaTSaPkoDoJycHCorK6MHHniAWJYlAGSxWOi+++6j4uJiWrRoERUWFtI777xDSUlJlJ2dTUOHDqXExESljvT0dNqxY4dqeyPv1Tol8jlWzQeEmorH41Gdr7t37wbP87jnnntw9OhRrFq1CmfPngUAiKKoGqK0Wi1mzJiBuXPnwmQyISsrC2VlZejq6sKiRYtQWVmJlA=='} Exception in thread Thread-1: Traceback (most recent call last): File "C:\Users\PC\AppData\Local\Programs\Python\Python39\lib\threading.py", line 980, in _bootstrap_inner self.run() File "C:\Users\PC\AppData\Local\Programs\Python\Python39\lib\threading.py", line 917, in run self._target(*self._args, **self._kwargs) File "C:\Users\PC\Desktop\New folder (5)\server.py", line 11, in listen_for_messages message = json.loads(message) File "C:\Users\PC\AppData\Local\Programs\Python\Python39\lib\json\__init__.py", line 346, in loads return _default_decoder.decode(s) File "C:\Users\PC\AppData\Local\Programs\Python\Python39\lib\json\decoder.py", line 340, in decode raise JSONDecodeError("Extra data", s, end) json.decoder.JSONDecodeError: Extra data: line 1 column 1403 (char 1402)
客户端代码(client.py)
from PyQt6.QtWidgets import * from PyQt6.QtGui import * from PyQt6.QtCore import * import socket import threading import json import os import base64 class Window(QMainWindow): def __init__(self): super().__init__() self.show() self.fileBtn = QPushButton('file upload') self.fileBtn.clicked.connect(self.fileUpload) mainWidget = QWidget() layout = QVBoxLayout() mainWidget.setLayout(layout) layout.addWidget(self.fileBtn) self.setCentralWidget(mainWidget) self.connect() def connect(self): self.HOST = '127.0.0.1' self.PORT = 1234 self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client.connect((self.HOST, self.PORT)) def fileUpload(self): fileName, ok = QFileDialog.getOpenFileName(self) if ok: file = open(fileName , 'rb') file = file.read() _range = 1024 def reader(data): for i in range(0, len(data), _range): yield data[i:i+_range] lst = list(reader(file)) for elem in lst: dic = json.dumps({ "type":"fileUpload", "file":base64.b64encode(elem).decode('utf-8'), }) self.client.sendall(bytes(dic,encoding="utf-8")) app = QApplication([]) win = Window() app.exec()
服务端代码(server.py)
import socket import threading import json LISTENER_LIMIT = 5 HOST = '127.0.0.1' PORT = 1234 def listen_for_messages(client): while True: message = client.recv(2048).decode('utf-8') message = json.loads(message) print(message) def main(): server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) try: server.bind((HOST, PORT)) except: print(f"Unable to bind to host {HOST} and port {PORT}") server.listen(LISTENER_LIMIT) while 1: client,address = server.accept() threading.Thread(target=listen_for_messages, args=(client, )).start() if __name__ == '__main__': main()
问题原因及解决方案
问题根源
TCP是流式协议,没有数据包边界。recv(2048)可能一次接收到多个客户端发送的JSON数据包,或者一个完整的JSON被拆分成多次接收。而json.loads()只能解析单个JSON对象,当输入是多个JSON拼接的字符串时,就会抛出"Extra data"错误。
解决方案:添加数据包分隔符
最简单可靠的方法是给每个JSON数据包末尾添加**换行符\n**作为分隔符,服务端接收数据后按分隔符拆分,确保每次只解析一个完整的JSON。
1. 修改客户端发送逻辑
在生成的JSON字符串后添加换行符再发送:
# 原代码 # self.client.sendall(bytes(dic,encoding="utf-8")) # 修改后 self.client.sendall(bytes(dic + '\n', encoding="utf-8"))
2. 修改服务端接收逻辑
维护一个缓冲区,将每次接收的片段拼接起来,按换行符拆分出完整的JSON进行解析:
def listen_for_messages(client): buffer = "" while True: chunk = client.recv(2048).decode('utf-8') if not chunk: break # 客户端断开连接 buffer += chunk # 循环处理所有完整的JSON数据包 while '\n' in buffer: # 拆分出第一个完整的JSON,剩下的留在缓冲区 message, buffer = buffer.split('\n', 1) if message.strip(): # 跳过空内容 try: data = json.loads(message) print(data) except json.JSONDecodeError as e: print(f"解析JSON错误: {e}")
替代方案:发送数据包长度
更严谨的方式是先发送JSON数据包的字节长度(比如用固定4字节的整数),服务端先接收长度,再接收对应长度的字节进行解析。这种方式适合对可靠性要求极高的场景,但代码复杂度稍高。
内容的提问来源于stack exchange,提问作者coder
相关产品推荐
相关产品推荐

