使用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()
修改说明:
- 规范HTTP请求读取:新增
read_http_request函数循环读取数据,直到收到\r\n\r\n结束标记,确保完整接收握手请求。 - 跳过请求行解析:HTTP请求第一行是
GET / HTTP/1.1这类请求行,不属于header,因此从data[1:]开始解析header。 - 优化header分割:使用
split(":", 1)避免header值中包含冒号导致的解析错误。 - 维持连接存活:握手完成后进入循环接收数据,保持连接状态,浏览器的
onopen事件将正常触发。 - 完善错误处理:对非WebSocket请求返回400错误,并正确关闭连接。
使用你提供的浏览器控制台代码测试,现在应该能正常输出Connection open!日志。
内容的提问来源于stack exchange,提问作者shriek
相关产品推荐
相关产品推荐

