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

如何解决Python Socket构建的WebSocket服务器长数据(图片Base64)解析问题?

WebSocket长数据解析失败问题解决

先搞懂WebSocket帧格式的核心细节

WebSocket帧的关键结构(简化版,聚焦长数据相关部分):

0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-------+-+-------------+-------------------------------+
|F|R|R|R| opcode|M| Payload len |    Extended payload length    |
|I|S|S|S|  (4)  |A|     (7)     |             (16/64)           |
|N|V|V|V|       |S|             |   (if payload len==126/127)   |
| |1|2|3|       |K|             |                               |
+-+-+-+-+-------+-+-------------+ - - - - - - - - - - - - - - - +
|     Extended payload length continued, if payload len == 127  |
+ - - - - - - - - - - - - - - - +-------------------------------+
|                               |Masking-key, if MASK set to 1  |
+-------------------------------+-------------------------------+
| Masking-key (continued)       |          Payload Data         |
+-------------------------------+ - - - - - - - - - - - - - - - +
:                     Payload Data continued ...                :
+ - - - - - - - - - - - - - - - +-------------------------------+
|                     Payload Data continued ...                |
+---------------------------------------------------------------+

和长数据解析相关的核心点:

  • Payload len字段:7位值如果是126,后面跟16位无符号整数表示真实长度;如果是127,后面跟64位无符号整数(大端字节序,高32位为0)。很多自研服务器只处理0-125的短长度,直接忽略126/127的扩展逻辑,这是长数据解析失败的头号原因。
  • MASK位:客户端发送的帧必须置1,服务器要用后续4字节掩码对整个payload逐字节异或解密。如果只解密部分数据,或者没处理完整的长payload,会直接导致乱码。
  • FIN位:长数据可能被分片发送,FIN=0表示当前是中间帧,FIN=1表示最后一帧。服务器必须拼接所有分片的payload才能得到完整数据,忽略分片逻辑会导致长数据收不全。

针对Python Socket服务器的修复要点

结合常见的自研服务器漏洞,对应修复:

  1. 处理扩展长度逻辑
    读取7位payload len后,根据值判断是否需要读取扩展长度:
    import struct
    
    def get_payload_length(sock):
        payload_len = ord(sock.recv(1)) & 0x7F
        if payload_len == 126:
            # 读取2字节大端无符号整数
            payload_len = struct.unpack('!H', sock.recv(2))[0]
        elif payload_len == 127:
            # 读取8字节大端无符号整数
            payload_len = struct.unpack('!Q', sock.recv(8))[0]
        return payload_len
    
  2. 确保完整接收并解密payload
    socket.recv(n)可能只返回部分数据,长数据场景下必须循环接收至指定长度,再用掩码完整解密:
    def recv_all(sock, length):
        data = b''
        while len(data) < length:
            chunk = sock.recv(length - len(data))
            if not chunk:
                return None
            data += chunk
        return data
    
    # 解密逻辑
    mask = recv_all(sock, 4)
    payload_len = get_payload_length(sock)
    payload = recv_all(sock, payload_len)
    
    decoded_payload = bytearray()
    for i in range(payload_len):
        decoded_payload.append(payload[i] ^ mask[i % 4])
    
  3. 处理分片帧拼接
    持续接收直到收到FIN=1的帧,拼接所有分片的payload:
    def receive_full_message(sock):
        full_payload = bytearray()
        while True:
            first_byte = ord(sock.recv(1))
            fin = (first_byte & 0x80) != 0
            opcode = first_byte & 0x0F
            
            # 只处理文本(0x1)、二进制(0x2)和续帧(0x0),忽略ping/pong等控制帧
            if opcode not in [0x0, 0x1, 0x2]:
                continue
            
            payload_len = get_payload_length(sock)
            mask = recv_all(sock, 4)
            payload = recv_all(sock, payload_len)
            
            # 解密当前帧payload
            decoded = bytearray()
            for i in range(payload_len):
                decoded.append(payload[i] ^ mask[i % 4])
            full_payload.extend(decoded)
            
            if fin:
                break
        
        # 文本帧转字符串,二进制帧返回字节数组
        return full_payload.decode('utf-8') if opcode in [0x1, 0x0] else full_payload
    

测试验证

用Base64图片数据测试时,先确认客户端发送的是单帧还是分片帧:

  • 单帧场景:检查服务器是否正确处理了126/127的扩展长度
  • 分片场景:检查服务器是否拼接了所有FIN=0的中间帧payload

内容的提问来源于stack exchange,提问作者keimore

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 02:06:36