Python HTTPServer优雅关闭SSE客户端时的Socket错误处理
问题描述
使用Python 3.9.13基于SSE(Server-Sent Events)实现摄像头帧推送,服务器端do_GET方法配置如下:
self.send_response(200) self.send_header("Content-Type", "text/event-stream") self.send_header("Cache-Control", "no-cache") self.send_header("Connection", "keep-alive") self.end_headers()
随后通过循环检测新帧并发送:
counter = g_cam_viewer.counter - 1 while True: if counter < (g_cam_viewer.counter): counter = g_cam_viewer.counter _, image_data = cv2.imencode('.jpg', g_cam_viewer.last_frame) image_data_bytes = image_data.tobytes() # Send the JPEG image data as SSE data image_base64 = base64.b64encode(image_data_bytes) image_frame = b'data: ' + image_base64 + b'\n\n' try: self.wfile.write(image_frame) self.wfile.flush() except (OSError, socket.error) as e: print('stream3 got exception: ' + str(e)) return # client disconnected, release thread except Exception as e: print('stream2 got exception: ' + str(e)) return # client disconnected, release thread time.sleep(0.05) # allow new frame to be ready
客户端通过EventSource接收:
app.sse = new EventSource(app.stream_url); app.sse.onmessage = (e) => { app.frame_count += 1; console.log('frame count: ', app.frame_count); var data = `data:image/jpeg;base64,${e.data}`; app.ui.imgStream.attr('src', data); }; app.sse.onerror = (e) => { console.warn('error: ', e); };
当客户端调用app.sse.close()断开连接时,服务器先触发代码中捕获的预期错误:
stream3 got exception: [WinError 10053] An established connection was aborted by the software in your host machine
约1.5秒后出现socket服务器库的未处理错误:
Exception occurred during processing of request from ('127.0.0.1', 17079) Traceback (most recent call last): File "C:\Users\LabUser\...\lib\socketserver.py", line 316, in _handle_request_noblock self.process_request(request, client_address) File "C:\Users\LabUser\...\lib\socketserver.py", line 347, in process_request self.finish_request(request, client_address) File "C:\Users\LabUser\...\lib\socketserver.py", line 360, in finish_request self.RequestHandlerClass(request, client_address, self) File "C:\Users\LabUser\...\lib\socketserver.py", line 747, in __init__ self.handle() File "C:\Users\LabUser\...\lib\http\server.py", line 427, in handle self.handle_one_request() File "C:\Users\LabUser\...\lib\http\server.py", line 393, in handle_one_request self.raw_requestline = self.rfile.readline(65537) File "C:\Users\LabUser\...\lib\socket.py", line 704, in readinto return self._sock.recv_into(b) ConnectionAbortedError: [WinError 10053] An established connection was aborted by the software in your host machine
需要消除这个socket服务器库的未处理错误。
解决方案
这个未处理错误的根源是:http.server的handle方法在do_GET返回后,会尝试继续读取客户端的请求数据,但此时连接已被客户端关闭,进而抛出未捕获的异常。以下两种方法可解决该问题:
方法1:重写handle方法捕获异常
在自定义的请求处理器类中重写handle方法,直接拦截连接断开相关的异常:
class StreamHandler(http.server.BaseHTTPRequestHandler): def handle(self): try: super().handle() except (ConnectionAbortedError, OSError, socket.error): # 客户端主动断开连接,无需额外处理 pass
这种方式能覆盖所有连接断开场景,避免日志输出冗余的错误栈信息。
方法2:捕获写入异常时主动关闭socket
在do_GET的异常捕获块中,主动关闭当前连接的socket,阻止后续的读取操作:
try: self.wfile.write(image_frame) self.wfile.flush() except (OSError, socket.error) as e: print('stream3 got exception: ' + str(e)) # 主动关闭socket,终止后续请求处理流程 self.connection.close() return except Exception as e: print('stream2 got exception: ' + str(e)) self.connection.close() return
主动关闭连接后,handle方法后续尝试读取请求时会直接检测到连接已关闭,不会抛出未处理的异常。
补充说明
两种方法均有效:方法1通用性更强,适合处理多种连接断开场景;方法2更直接,在检测到写入失败时立即终止连接。如果服务需要处理大量SSE长连接,推荐使用方法1,避免遗漏异常场景。
内容的提问来源于stack exchange,提问作者ishahak
相关产品推荐
相关产品推荐

