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

WebSocket+OpenCV扫码:连接关闭时终止摄像头读取循环的方案

问题分析

你的核心问题是内层摄像头扫描循环为同步阻塞逻辑,且未主动检查WebSocket连接状态——只有执行await websocket.recv()或await websocket.send()时才会触发ConnectionClosed异常,但内层的cap.read()和cv2.waitKey(1)会持续占用线程,导致连接关闭时无法立即终止循环,必须等下一次条码扫描完成才能退出。

同时,每次收到客户端消息就重新初始化摄像头的做法也存在效率问题,频繁启停摄像头会增加系统开销。

解决方案

1. 主动检查连接状态,即时终止循环

在内层扫描循环的每次迭代中,主动检查WebSocket是否已关闭,一旦检测到关闭状态,立即退出循环并释放资源。

2. 优化摄像头生命周期

将摄像头的初始化移至连接建立阶段(外层循环之前),连接关闭时再统一释放,避免重复初始化的开销。

3. 避免阻塞异步事件循环

OpenCV的cap.read()和cv2.waitKey属于同步操作,会阻塞asyncio事件循环。使用asyncio.to_thread()将同步的摄像头操作放到单独线程中,保证WebSocket任务的响应性。

修改后的代码
import asyncio
import websockets
import cv2
from pyzbar.pyzbar import decode  # 假设你使用pyzbar库的decode函数

def searchProductId(barcode_data):
    # 保留你的商品ID查询逻辑
    return "12345"  # 示例返回值

async def handler(websocket, path):
    cap = None
    try:
        # 连接建立时初始化摄像头,仅执行一次
        cap = cv2.VideoCapture(0)
        cap.set(3, 640)
        cap.set(4, 480)
        
        while True:
            # 等待客户端指令
            data = await websocket.recv()
            if not data:
                continue
            
            scanning = True
            while scanning:
                # 检查连接状态,已关闭则终止扫描
                if websocket.closed:
                    scanning = False
                    break
                
                # 用to_thread封装同步操作,避免阻塞事件循环
                _, img = await asyncio.to_thread(cap.read)
                
                # 异步执行条码解码
                barcodes = await asyncio.to_thread(decode, img)
                for barcode in barcodes:
                    barcodeData = barcode.data.decode('utf-8')
                    productId = searchProductId(barcodeData)
                    
                    if productId != -1:
                        await websocket.send(productId)
                        await asyncio.sleep(1)
                        productId = -1
                        # 可选:找到条码后停止扫描,等待下一次客户端指令
                        scanning = False
                        break
                
                # 异步显示画面(不需要可删除)
                await asyncio.to_thread(cv2.imshow, 'Result', img)
                # 异步处理按键输入,避免阻塞
                key = await asyncio.to_thread(cv2.waitKey, 1)
                # 按ESC键手动停止扫描(可选)
                if key == 27:
                    scanning = False
                    
    except websockets.ConnectionClosed:
        print("连接已关闭!")
    finally:
        # 确保无论正常退出还是异常,都释放摄像头资源
        if cap is not None and cap.isOpened():
            cap.release()
        cv2.destroyAllWindows()

start_server = websockets.serve(handler, "localhost", 8000)

asyncio.get_event_loop().run_until_complete(start_server)
asyncio.get_event_loop().run_forever()
关键改进点说明
  • 实时连接状态检查:在内层扫描循环中加入if websocket.closed:判断,连接关闭时立即终止循环。
  • 摄像头复用:连接建立时仅初始化一次摄像头,减少系统资源消耗。
  • 异步兼容处理:用asyncio.to_thread()封装所有同步OpenCV操作,保证WebSocket事件循环不被阻塞。
  • 安全资源释放:通过finally块确保摄像头资源在任何情况下都能被正确释放。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.30 23:42:31