Python socket接收POST JSON数据偶发截断解析失败问题
问题根因
JSON随机截断、解析失败是socket使用不符合HTTP协议规范导致的,核心问题有两个:
- 单次
recv调用无法获取完整请求:TCP是面向字节流的协议,不存在数据包边界。单次执行recv(1048576)仅会返回当前内核接收缓冲区中已就绪的字节,可能是半段请求头、也可能是半截JSON内容,和设置的缓冲区大小没有关系。TCP分片、网络延迟、发送端拆包都会导致单次调用拿不全数据,这也是丢失块大小无规律的核心原因。 - 正则硬编码删除请求头的逻辑不可靠:HTTP请求头的长度、字段数量是动态变化的,不同请求携带的Header数量不一致,硬编码匹配POST开头删除固定8~10行内容的逻辑,要么多删切掉JSON开头内容,要么少删把Header字段混入JSON字符串,同样会触发
json.loads报错。
修复方案
1. 实现符合HTTP规范的完整请求接收逻辑
必须分阶段循环读取数据,禁止单次recv直接拿全量数据,流程如下:
- 循环读取数据,直到读到
\r\n\r\n分隔符,拿到完整的HTTP请求头 - 从请求头中解析
Content-Length字段,获取后续JSON请求体的总字节长度 - 继续循环读取数据,直到累计接收的请求体长度等于
Content-Length标注的值,此时拿到的才是完整的JSON字符串
可直接替换原有接收逻辑的代码如下:
def recv_full_request(client_socket): buffer = b"" # 读取完整请求头 while b"\r\n\r\n" not in buffer: chunk = client_socket.recv(4096) if not chunk: return None, None buffer += chunk # 拆分请求头和已接收的部分请求体 header_raw, partial_body = buffer.split(b"\r\n\r\n", 1) header_str = header_raw.decode("utf-8") # 解析Content-Length content_length = 0 for line in header_str.split("\r\n"): if line.lower().startswith("content-length:"): content_length = int(line.split(":", 1)[1].strip()) break # 读取剩余的所有请求体内容 body = partial_body while len(body) < content_length: chunk = client_socket.recv(4096) if not chunk: break body += chunk return header_str, body.decode("utf-8")
原有主循环中data = client_socket.recv(1048576).decode('utf-8')替换为调用该函数,从返回值中分别拿到请求头和纯JSON体,Token校验直接从请求头里取X-Gitlab-Token字段即可,不用再写正则从全量数据里匹配。
2. 移除硬编码的请求头清理正则
before_json模块中6行匹配POST开头、删除固定行数内容的正则代码可以全部删除,经过上述接收逻辑处理后,传入解析函数的请求体已经是剥离了所有HTTP头的纯JSON内容,直接调用json.loads即可,不会出现头内容混入的问题。
3. 修复其他隐性逻辑bug
现有代码遍历added、modified列表时,一边遍历一边执行remove操作会触发列表索引偏移,导致元素漏处理,替换为列表推导式过滤即可:
# 替换原有嵌套循环删除元素的逻辑 added = [item for item in keys['added'] if 'path to file' in item] modified = [item for item in keys['modified'] if item]
另外现有代码中404响应的状态行写为HTTP/1.1 404 OK不符合规范,正确短语为Not Found,不影响核心功能可按需调整。
内容的提问来源于stack exchange,提问作者Miekrif
相关产品推荐
相关产品推荐

