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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.06 13:54:52