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

使用Python原生socket实现WebSocket服务器无法建立连接求助

问题分析与解决方案

你的WebSocket握手逻辑框架正确,但连接失败的核心原因是发送完握手响应后,服务器立即关闭了连接,导致浏览器无法维持WebSocket连接。此外,请求解析环节存在小瑕疵,可能引发极端场景下的解析错误。

具体问题点:

  • 处理完握手响应后,当前连接对象conn未被保留,当循环进入下一次accept()时,前一个conn会被Python垃圾回收机制自动关闭,浏览器刚收到响应就丢失了连接。
  • 使用recv(1024).strip()可能意外截断请求或丢失HTTP请求结束标记\r\n\r\n,虽握手请求通常较短,但这不符合HTTP请求读取的规范。

修改后的代码:

import socket
from base64 import b64encode
from hashlib import sha1

PORT = 8082

def response_key(key):
    GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" # 标准魔术字符串
    hash_obj = sha1(key.encode() + GUID.encode())
    response_key = b64encode(hash_obj.digest()).strip()
    return response_key.decode("ASCII")

def shake_hand(key):
    response = (
        "HTTP/1.1 101 Switching Protocols\r\n"
        "Upgrade: websocket\r\n"
        "Connection: Upgrade\r\n"
        f"Sec-WebSocket-Accept: {response_key(key)}\r\n"
        "\r\n"
    )
    return response

def read_http_request(conn):
    # 规范读取HTTP请求直到结束标记\r\n\r\n
    request_data = b""
    while b"\r\n\r\n" not in request_data:
        chunk = conn.recv(1024)
        if not chunk:
            return None
        request_data += chunk
    return request_data.decode()

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
    s.bind(("localhost", PORT))
    s.listen()
    print(f"WebSocket server listening on ws://localhost:{PORT}")
    while True:
        conn, addr = s.accept()
        print(f"New connection from {addr}")
        
        request = read_http_request(conn)
        if not request:
            conn.close()
            continue
            
        data = request.split("\r\n")
        headers = {}
        # 跳过第一行请求行(如GET / HTTP/1.1)
        for h in data[1:]:
            h = h.strip()
            if not h:
                continue
            header_parts = h.split(":", 1)  # 用split(...,1)避免header值含冒号的情况
            if len(header_parts) == 2:
                key, value = map(lambda x: x.strip().lower(), header_parts)
                headers[key] = value

        if (headers.get("connection") == "upgrade") and (headers.get("upgrade") == "websocket"):
            handshake_response = shake_hand(headers["sec-websocket-key"])
            conn.sendall(handshake_response.encode())
            print("Handshake completed")
            # 保持连接,可在此添加WebSocket帧处理逻辑
            # 目前简单维持连接,接收客户端发送的帧(不处理)
            try:
                while True:
                    # 接收WebSocket最小帧(至少2字节)
                    frame = conn.recv(2)
                    if not frame:
                        break
            except Exception as e:
                print(f"Connection error: {e}")
            finally:
                conn.close()
                print(f"Connection with {addr} closed")
        else:
            # 返回HTTP错误响应
            error_response = "HTTP/1.1 400 Bad Request\r\n\r\n"
            conn.sendall(error_response.encode())
            conn.close()

修改说明:

  1. 规范HTTP请求读取:新增read_http_request函数循环读取数据,直到收到\r\n\r\n结束标记,确保完整接收握手请求。
  2. 跳过请求行解析:HTTP请求第一行是GET / HTTP/1.1这类请求行,不属于header,因此从data[1:]开始解析header。
  3. 优化header分割:使用split(":", 1)避免header值中包含冒号导致的解析错误。
  4. 维持连接存活:握手完成后进入循环接收数据,保持连接状态,浏览器的onopen事件将正常触发。
  5. 完善错误处理:对非WebSocket请求返回400错误,并正确关闭连接。

使用你提供的浏览器控制台代码测试,现在应该能正常输出Connection open!日志。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 00:05:41