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

C#与Fast API WebSocket通信帧率极低问题排查

问题描述

我实现了一套C#客户端与FastAPI服务端的WebSocket通信程序:
C#客户端捕获摄像头实时帧,发送至FastAPI服务端,服务端将图像回传给C#客户端并在PictureBox中显示。本示例中FastAPI未对图像做处理,仅用于测试通信流程,但实际场景中会进行图像处理。

C#(客户端)代码

private void CaptureCameraCallback(int camInd, PictureBox pictureBox)
{
    // 创建WebSocket连接
    using (var ws = new WebSocketSharp.WebSocket("ws://localhost:8000/ws"))
    {
        // 连接到WebSocket服务器
        ws.Connect();

        // 创建视频捕获对象
        using (var capture = new VideoCapture(camInd))
        {
            // 循环捕获帧并发送到服务端
            while (true)
            {
                // 创建Mat对象存储帧
                using (var frame = new Mat())
                {
                    // 读取摄像头下一帧
                    capture.Read(frame);

                    // 检查帧是否为空
                    if (!frame.Empty())
                    {
                        // 将帧编码为JPEG
                        var buffer = new byte[frame.Width * frame.Height * frame.ElemSize()];
                        Cv2.ImEncode(".jpg", frame, out buffer);

                        // 发送图像到服务端
                        ws.Send(buffer);

                        // 监听服务端响应
                        ws.OnMessage += (sender, e) =>
                        {
                            // 将服务端响应转换为图像
                            var responseBytes = e.RawData;
                            using (var responseStream = new MemoryStream(responseBytes))
                            {
                                var responseImage = Image.FromStream(responseStream);

                                // 在UI线程显示图像
                                pictureBox.Invoke((MethodInvoker)(() => {
                                    pictureBox.Image = responseImage;
                                }));
                            }
                        };
                    }
                    else
                    {
                        // 帧为空时记录错误并继续
                        Debug.WriteLine("无法捕获帧");
                        continue;
                    }
                }
            }
        }
    }
}

FastAPI(服务端)代码

from fastapi import FastAPI, WebSocket
import cv2
import numpy as np

class WebSocketServer:
    def __init__(self):
        self.app = FastAPI()

        @self.app.websocket("/ws")
        async def websocket_endpoint(websocket: WebSocket):
            print("接受连接")
            await websocket.accept()
            print('已接受')
            while True:
                try:
                    image_bytes = await websocket.receive_bytes()
                    nparr = np.frombuffer(image_bytes, np.uint8)
                    img = cv2.imdecode(nparr, cv2.IMREAD_COLOR)
                    
                    # 此处可添加图像处理逻辑
                    # ...
                    # 回传处理后的图像
                    _, buffer = cv2.imencode('.jpg', img)
                    response_bytes = buffer.tobytes()
                    await websocket.send_bytes(response_bytes)
                except:
                    pass
                    break
    
    def run(self, host: str = '0.0.0.0', port: int = 8000):
        import uvicorn
        uvicorn.run(self.app, host=host, port=port)

if __name__ == '__main__':
    server = WebSocketServer()
    server.run()

# 启动命令:uvicorn fastApi:app --reload

图像能成功收发,但帧率极低(不足1FPS),我怀疑是PictureBox的图像处理或显示方式有误,请问问题出在哪?


问题分析与解决方案

核心问题1:重复绑定OnMessage事件

每一次循环捕获帧时都重新绑定ws.OnMessage事件,导致同一个事件被注册几十上百次。每次服务端返回响应,所有绑定的事件处理函数都会执行,大量冗余UI更新直接拖慢帧率。

核心问题2:UI线程阻塞与低效更新

  • 循环捕获帧的逻辑在主线程执行,未使用异步或多线程,导致UI线程被阻塞,无法及时响应刷新请求。
  • 每次直接替换pictureBox.Image,未释放旧图像资源,易引发内存泄漏,间接影响性能;且Image.FromStream依赖的流关闭后图像可能失效。

核心问题3:WebSocket同步发送阻塞

ws.Send(buffer)是同步调用,等待服务端响应时当前线程被挂起,无法继续捕获下一张帧,这是帧率低的关键原因之一。


修复后的C#客户端代码

private async void CaptureCameraCallback(int camInd, PictureBox pictureBox)
{
    using (var ws = new WebSocketSharp.WebSocket("ws://localhost:8000/ws"))
    {
        // 仅绑定一次OnMessage事件
        ws.OnMessage += (sender, e) =>
        {
            var responseBytes = e.RawData;
            using (var responseStream = new MemoryStream(responseBytes))
            {
                // 使用Bitmap并克隆图像,避免流关闭后图像失效
                using (var responseBitmap = new Bitmap(responseStream))
                {
                    var displayBitmap = (Bitmap)responseBitmap.Clone();
                    
                    pictureBox.Invoke((MethodInvoker)(() =>
                    {
                        // 释放旧图像资源,避免内存泄漏
                        pictureBox.Image?.Dispose();
                        pictureBox.Image = displayBitmap;
                    }));
                }
            }
        };

        ws.Connect();
        using (var capture = new VideoCapture(camInd))
        {
            // 将捕获逻辑放到后台线程,避免阻塞UI
            await Task.Run(() =>
            {
                while (ws.IsAlive)
                {
                    using (var frame = new Mat())
                    {
                        if (!capture.Read(frame))
                        {
                            Debug.WriteLine("无法捕获帧");
                            continue;
                        }

                        // 设置JPEG质量参数,平衡传输速度与图像质量
                        var encodeParams = new List<int> { (int)ImwriteFlags.JpegQuality, 70 };
                        Cv2.ImEncode(".jpg", frame, out var buffer, encodeParams);

                        // 使用异步发送,避免阻塞捕获线程
                        ws.SendAsync(buffer, (success) =>
                        {
                            if (!success)
                            {
                                Debug.WriteLine("发送失败");
                            }
                        });

                        // 控制捕获频率,避免发送过快导致拥塞
                        Thread.Sleep(30); // 约30FPS
                    }
                }
            });
        }
    }
}

额外优化建议

  1. 服务端优化:
    • 移除未使用的StreamingResponse导入。
    • 捕获异常时添加日志记录,不要直接pass,方便排查连接断开问题。
  2. 图像传输优化:
    • 根据实际需求降低摄像头分辨率,减少单帧数据量。
    • 调整JPEG质量参数,在可接受的画质范围内尽量减小传输体积。
  3. 线程管理:
    • 使用CancellationToken控制循环终止,避免强制退出导致资源泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.23 05:22:44