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
相关产品推荐
相关产品推荐

