C# TCP屏幕共享程序“Parameter is not valid”异常求助
解决TCP屏幕共享中图片字节不完整的问题
你的核心问题是TCP的流特性导致没有天然消息边界,服务器无法区分单张图片的起始和结束,读取的字节可能是不完整的单张或多张图片的混合,从而转图片时抛出ArgumentException。直接用Thread.Sleep是低效且不可靠的,正确的做法是自定义消息协议:先发送图片数据的长度,再发送图片内容,确保服务器能精准读取完整的单张图片数据。
解决方案实现
客户端修改(发送端)
- 把
NetworkStream的获取移到循环外,避免重复创建 - 先发送图片字节数组的长度(用4字节int存储),再发送图片数据
- 及时释放
MemoryStream、Bitmap等资源,避免内存泄漏
private void sendScreen() { try { using (TcpClient client = new TcpClient("192.168.0.102", 4567)) using (NetworkStream streamScreen = client.GetStream()) { while (true) { // 截取屏幕 using (Bitmap bm = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height)) using (Graphics g = Graphics.FromImage(bm)) { g.CopyFromScreen(0, 0, 0, 0, bm.Size); // 压缩图片(可选,减少传输量) using (Bitmap newBM = new Bitmap(bm, new Size(bm.Width / 2, bm.Height / 2))) using (MemoryStream ms = new MemoryStream()) { newBM.Save(ms, ImageFormat.Jpeg); byte[] imageBytes = ms.ToArray(); // 第一步:发送图片字节长度(4字节int) byte[] lengthBytes = BitConverter.GetBytes(imageBytes.Length); streamScreen.Write(lengthBytes, 0, lengthBytes.Length); // 第二步:发送图片数据 streamScreen.Write(imageBytes, 0, imageBytes.Length); } } Thread.Sleep(500); // 控制帧率,可根据需求调整 } } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } }
服务器修改(接收端)
- 先读取4字节的长度数据,确保读满4字节(处理TCP拆包)
- 根据长度读取对应字节数的图片数据,循环读取直到获取完整数据
- 用完整的字节数组转换图片,及时释放旧的
Image资源
private void receiveScreenShare() { TcpListener list = new TcpListener(IPAddress.Parse("192.168.0.102"), 4567); list.Start(); using (TcpClient client = list.AcceptTcpClient()) using (NetworkStream streamScreenShare = client.GetStream()) { while (true) { try { // 第一步:读取图片长度(4字节) byte[] lengthBuffer = new byte[4]; int bytesRead = 0; // 循环读取直到拿到完整的4字节长度 while (bytesRead < 4) { int read = streamScreenShare.Read(lengthBuffer, bytesRead, 4 - bytesRead); if (read == 0) { // 客户端断开连接 return; } bytesRead += read; } int imageLength = BitConverter.ToInt32(lengthBuffer, 0); // 第二步:读取完整的图片数据 byte[] imageBytes = new byte[imageLength]; bytesRead = 0; while (bytesRead < imageLength) { int read = streamScreenShare.Read(imageBytes, bytesRead, imageLength - bytesRead); if (read == 0) { // 客户端断开连接 return; } bytesRead += read; } // 转换并显示图片 using (MemoryStream ms = new MemoryStream(imageBytes)) { Image newImage = Image.FromStream(ms); // 释放旧图片资源,避免内存泄漏 pictureBox1.Image?.Dispose(); pictureBox1.Image = newImage; } } catch (Exception ex) { Console.WriteLine(ex.ToString()); } } } }
关键说明
- 消息边界定义:用4字节int存储图片长度,是TCP传输中区分消息的标准做法,能精准解决粘包/拆包问题
- 循环读取:
NetworkStream.Read()返回的是实际读取的字节数,必须循环读取直到获取完整的长度或图片数据,避免因TCP拆包导致数据不完整 - 资源释放:所有实现
IDisposable的对象(如TcpClient、Bitmap、MemoryStream)都用using语句自动释放,避免内存泄漏 - 效率:这种方式不需要等待固定时间,完全基于实际数据传输完成情况,比
Thread.Sleep高效且可靠
内容的提问来源于stack exchange,提问作者karan ugale
相关产品推荐
相关产品推荐

