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

Python实现基础WebSocket服务器:握手后向客户端发送消息时出现无效帧头错误的排查与解决

问题分析与修复

你的问题主要出在WebSocket握手响应的格式错误,以及发送响应时的一个小细节,导致浏览器无法正确完成握手,进而后续的帧解析报错。

具体错误点:

1. HTTP握手响应缺少结束标记

WebSocket的握手基于HTTP协议,HTTP响应要求在最后一个头部字段后必须有两个连续的\r\n(空行)来标记头信息结束。你的代码中发送的响应最后只有一个换行,导致浏览器无法正确识别握手完成,后续的WebSocket帧会被错误解析。

2. Sec-WebSocket-Accept值包含多余换行

base64.encodebytes()函数返回的字符串末尾会自动添加一个\n换行符,直接拼接到响应头里会导致Sec-WebSocket-Accept的值不符合规范,这也会影响握手的正确性。

3. (非致命但建议修正)HTTP头字段的规范写法

虽然HTTP头字段名不区分大小写,但规范里Connection和Upgrade字段值的首字母应该大写(Connection: Upgrade、Upgrade: websocket),避免潜在的兼容性问题。

修复后的代码

import socket
import threading
import hashlib
from time import sleep
from base64 import encodebytes

bind_ip = '127.0.0.1'
bind_port = 8000

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.bind((bind_ip, bind_port))
server.listen(5)

print('Listening on {}:{}'.format(bind_ip, bind_port))

def decodeMessage(bytesArr):
    if (bytesArr[0] & 0xF == 0x1):
        unmasked = bytearray()
        mask = bytesArr[2:6]  # 提取掩码字节
        payload = bytesArr[6:]  # 提取负载内容
        for i in range(len(payload)):
            unmasked.append(payload[i] ^ mask[i % 4])
        return unmasked

def handle_client_connection(client_socket):
    request = client_socket.recv(1024)
    headers = request.split(b"\r\n")
    key = b""
    for header in headers:
        if header.startswith(b"Sec-WebSocket-Key:"):
            key = header.split(b": ")[1]  # 更可靠的提取方式
    # 计算Accept key,去掉encodebytes带来的换行
    accept_key = encodebytes(hashlib.sha1(key + b"258EAFA5-E914-47DA-95CA-C5AB0DC85B11").digest()).strip()
    # 构造正确的握手响应,末尾加上\r\n\r\n
    response = b"HTTP/1.1 101 Switching Protocols\r\n" \
               b"Connection: Upgrade\r\n" \
               b"Upgrade: websocket\r\n" \
               b"Sec-WebSocket-Accept: %s\r\n\r\n" % accept_key
    client_socket.send(response)
    
    msg = decodeMessage(client_socket.recv(1024))
    print(msg.decode('utf-8'))  # 解码为字符串输出更友好
    
    # 发送正确的单帧文本消息,这里的格式是对的,握手修复后就能正常工作
    client_socket.send(b"\x81\x05Hello")

while True:
    try:
        client_sock, address = server.accept()
        print('Accepted connection from {}:{}'.format(address[0], address[1]))
        client_handler = threading.Thread(target=handle_client_connection, args=(client_sock,))
        client_handler.start()
    except KeyboardInterrupt:
        print("Server shutting down...")
        server.close()
        break

额外优化说明:

  • 优化了decodeMessage函数的实现,直接提取掩码和负载,避免创建不必要的大字节数组,逻辑更清晰。
  • 提取Sec-WebSocket-Key时改用startswith和split,比find更可靠。
  • 打印消息时用decode('utf-8')转为字符串,输出更易读。

这样修复后,浏览器应该能正确完成握手,并且接收服务器发送的消息,不会再出现invalid frame header错误了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 19:02:40