使用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
相关产品推荐
相关产品推荐

