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

使用Socket传输图像时出现std::out_of_range错误如何解决?

问题核心原因分析

你遇到的报错是底层C++实现抛出的越界错误,结合代码逻辑,核心问题有3个:

  • TCP是流协议无消息边界,你当前直接发送原始图像字节,接收端单次Receive不一定能收完整1601203=57600字节的一帧数据,当实际收到的数据长度不足时,你按固定偏移取像素的逻辑会访问超出数组范围的内存,触发越界错误
  • Python端直接发送OpenCV Mat对象,Mat的内存布局带行对齐填充,不是连续的按像素排列的BGR字节,发送的内存数据长度和你预期的像素总长度不匹配,进一步加重数据错位问题
  • C#端每次循环都新建Socket连接,频繁建连断连容易出现残包、空包的情况,某次连接没收到数据时,你还是照常访问空字节数组的下标,直接触发报错
修复方案

1. Python服务端修改

发送前先转连续字节数组,增加固定4字节的帧头标识图像长度,再发送实际图像数据,确保两端数据对齐:

#!/usr/bin/env python3
import socket
import cv2
import struct

serversocket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
serversocket.bind(("XXX.XXX.X.XXX", 9999))
serversocket.listen(5)
print ('server started and listening')
while 1:
    (clientsocket, address) = serversocket.accept()
    print ("connection found!")
    img = cv2.imread('testImage.jpeg')
    # 统一尺寸为C#端需要的160*120,避免尺寸不匹配
    img = cv2.resize(img, (160,120))
    if img is not None and img.size > 0:
        # 转换为连续字节数组
        img_bytes = img.tobytes()
        # 先发4字节大端序的帧长度
        clientsocket.sendall(struct.pack('>I', len(img_bytes)))
        # 再发实际图像数据
        clientsocket.sendall(img_bytes)
    clientsocket.close()

2. C#客户端修改

接收时先收帧头获取预期长度,收满完整一帧再处理图像,增加异常捕获避免崩溃:

private void btnImagem_Click(object sender, EventArgs e)
{
    stream = stream == 0 ? 1 : 0;
    const int EXPECT_FRAME_SIZE = 160 * 120 * 3;
    while (stream == 1)
    {
        using (Socket receiveSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
        {
            try
            {
                IPEndPoint hostIpEndPoint = new IPEndPoint(IPAddress.Parse("XXX.XXX.X.XXX"), 9999);
                receiveSocket.Connect(hostIpEndPoint);
                
                // 先收4字节帧头
                byte[] lenBuffer = new byte[4];
                int lenRecvCount = receiveSocket.Receive(lenBuffer);
                if (lenRecvCount != 4) continue;
                // 转换大端序为本地字节序
                if (BitConverter.IsLittleEndian) Array.Reverse(lenBuffer);
                int frameLen = BitConverter.ToInt32(lenBuffer, 0);
                if (frameLen != EXPECT_FRAME_SIZE) continue; // 长度不匹配直接丢弃
                
                // 循环收满完整一帧
                byte[] frameBuffer = new byte[EXPECT_FRAME_SIZE];
                int totalRecv = 0;
                while (totalRecv < EXPECT_FRAME_SIZE)
                {
                    int curRecv = receiveSocket.Receive(frameBuffer, totalRecv, EXPECT_FRAME_SIZE - totalRecv, SocketFlags.None);
                    if (curRecv == 0) break; // 连接断开直接退出
                    totalRecv += curRecv;
                }
                if (totalRecv != EXPECT_FRAME_SIZE) continue;
                
                // 处理图像
                Bitmap bmp2 = new Bitmap(160, 120);
                int t = 0;
                for (int y = 0; y < 120; y++)
                {
                    for (int x = 0; x < 160; x++)
                    {
                        bmp2.SetPixel(x, y, Color.FromArgb(frameBuffer[t+2], frameBuffer[t+1], frameBuffer[t]));
                        t += 3;
                    }
                }
                videoBox.Image = bmp2;
            }
            catch
            {
                // 异常跳过本次循环,避免程序直接崩溃
                continue;
            }
        }
        wait(10);
    }
    videoBox.Image = null;
}
额外优化建议
  • 如果需要做实时流传输,不要频繁新建关闭连接,保持长连接连续发帧即可,性能会高很多
  • 发送端可以将图像编码为JPEG格式后再传输,大幅减小数据量,降低粘包概率,接收端直接解码为Bitmap即可,不需要手动拼接像素
  • 你当前使用的wait函数依赖Application.DoEvents,容易导致UI线程重入问题,建议改用异步或者BackgroundWorker做Socket接收逻辑,避免阻塞UI线程

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 05:06:03