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

C# TCP屏幕共享程序“Parameter is not valid”异常求助

解决TCP屏幕共享中图片字节不完整的问题

你的核心问题是TCP的流特性导致没有天然消息边界,服务器无法区分单张图片的起始和结束,读取的字节可能是不完整的单张或多张图片的混合,从而转图片时抛出ArgumentException。直接用Thread.Sleep是低效且不可靠的,正确的做法是自定义消息协议:先发送图片数据的长度,再发送图片内容,确保服务器能精准读取完整的单张图片数据。

解决方案实现

客户端修改(发送端)

  1. 把NetworkStream的获取移到循环外,避免重复创建
  2. 先发送图片字节数组的长度(用4字节int存储),再发送图片数据
  3. 及时释放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());
    }
}

服务器修改(接收端)

  1. 先读取4字节的长度数据,确保读满4字节(处理TCP拆包)
  2. 根据长度读取对应字节数的图片数据,循环读取直到获取完整数据
  3. 用完整的字节数组转换图片,及时释放旧的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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 14:12:06