Python3下Socket单行JSON通信解码异常的修复方案
问题
将应用移植至Python3环境,需采用UTF-8编码实现Socket通信:客户端连接服务器后发送一行JSON编码数据,读取服务器返回的一行JSON编码数据并持续通信。
服务器监听端口代码
def handle_oracle_query(self, sock, address): sockIn = sock.makefile('rb') sockOut = sock.makefile('wb') line = sockIn.readline() submitted_data = json.loads(line) self.get_thread_specific_storage()['environmental_variables'] = submitted_data['environmental_variables'] self.get_thread_specific_storage()['cgi'] = submitted_data['cgi'] generate_output() print_output(sockOut) sock.close() sockIn.close() sockOut.close() self.remove_thread_specific_storage()
客户端连接代码
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ip = server[0] try: port = int(server[1]) except: port = DEFAULT_PORT sock.connect((ip, port)) sockIn = sock.makefile("rb") sockOut = sock.makefile("wb") cgi_hash = {} for key in cgi.FieldStorage().keys(): cgi_hash[key] = cgi.FieldStorage()[key].value as_dictionary = dict(os.environ) submitted_data = { 'environmental_variables': as_dictionary, 'cgi': cgi_hash} encoded_data = json.dumps(submitted_data) sys.stderr.write(encoded_data + '\r\n') sockOut.write(encoded_data + '\r\n') sockOut.flush() result = json.loads(sockIn.read()) sock.close() sockIn.close() sockOut.close()
运行异常栈
Traceback (most recent call last): File "/home/christos/fathers/bin/./fathersd", line 4075, in <module> multitasking.start_oracle() File "/home/christos/fathers/bin/./fathersd", line 2396, in start_oracle self.run_oracle() File "/home/christos/fathers/bin/./fathersd", line 2372, in run_oracle self.handle_oracle_query(newsocket, address) File "/home/christos/fathers/bin/./fathersd", line 2309, in handle_oracle_query submitted_data = json.loads(line) File "/usr/lib/python3.10/json/__init__.py", line 346, in loads return _default_decoder.decode(s) File "/usr/lib/python3.10/json/decoder.py", line 337, in decode obj, end = self.raw_decode(s, idx=_w(s, 0).end()) File "/usr/lib/python3.10/json/decoder.py", line 355, in raw_decode raise JSONDecodeError("Expecting value", s, err.value) from None json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
解决方案
核心问题分析
Python3中字节串(bytes)与字符串(str)严格区分,原代码未正确处理编码转换:客户端直接发送字符串拼接字节换行符,服务器读取字节串后未解码就传入json.loads,导致解析失败。
客户端修改点
- 改用文本模式处理Socket流:创建
makefile时指定UTF-8编码,自动完成字符串与字节的转换,避免手动编码错误。 - 修正数据发送逻辑:文本模式下直接写入JSON字符串,无需手动拼接字节换行符。
- 按行读取服务器返回:原代码
sockIn.read()会读取所有数据,不符合单行JSON需求,改用readline()读取单行内容。
修改后的客户端代码:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ip = server[0] try: port = int(server[1]) except: port = DEFAULT_PORT sock.connect((ip, port)) # 文本模式+UTF-8编码,自动处理字符串/字节转换 sockIn = sock.makefile("r", encoding='utf-8') sockOut = sock.makefile("w", encoding='utf-8') cgi_hash = {} for key in cgi.FieldStorage().keys(): cgi_hash[key] = cgi.FieldStorage()[key].value as_dictionary = dict(os.environ) submitted_data = { 'environmental_variables': as_dictionary, 'cgi': cgi_hash} encoded_data = json.dumps(submitted_data) sys.stderr.write(f"{encoded_data}\r\n") # 文本模式直接写字符串,自动编码为UTF-8字节 sockOut.write(f"{encoded_data}\r\n") sockOut.flush() # 按行读取单行JSON返回 result_line = sockIn.readline() result = json.loads(result_line) sock.close() sockIn.close() sockOut.close()
服务器修改点
- 改用文本模式处理Socket流:同样指定UTF-8编码,直接读取字符串,避免手动解码操作。
- 增加防御性判断:读取到空行时(客户端异常断开)直接终止流程,避免JSON解析报错。
- 确保输出刷新:发送数据后主动刷新缓冲区,保证数据及时传输到客户端。
修改后的服务器代码:
def handle_oracle_query(self, sock, address): # 文本模式+UTF-8编码,直接操作字符串 sockIn = sock.makefile('r', encoding='utf-8') sockOut = sock.makefile('w', encoding='utf-8') line = sockIn.readline() # 防御客户端断开导致的空行 if not line: sock.close() return submitted_data = json.loads(line) self.get_thread_specific_storage()['environmental_variables'] = submitted_data['environmental_variables'] self.get_thread_specific_storage()['cgi'] = submitted_data['cgi'] generate_output() # print_output需输出单行JSON字符串,文本模式自动编码为UTF-8 print_output(sockOut) sockOut.flush() sock.close() sockIn.close() sockOut.close() self.remove_thread_specific_storage()
额外注意事项
- 确保
generate_output和print_output函数生成单行JSON字符串,避免换行符导致客户端解析错误。 - 可使用
with语句自动管理Socket和文件对象的生命周期,避免手动关闭遗漏:with sock.makefile('r', encoding='utf-8') as sockIn, sock.makefile('w', encoding='utf-8') as sockOut: # 处理逻辑
内容的提问来源于stack exchange,提问作者Christos Hayward
相关产品推荐
相关产品推荐

