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

C#基于TCP协议实现屏幕捕获流传输时复杂画面失真问题咨询

问题根本原因

你遇到的是TCP流传输的典型粘包/拆包问题:TCP是面向流的协议,本身没有数据帧边界,你直接发送的PNG字节流长度不固定:

  • 屏幕内容简单时,PNG压缩后体积很小,发送端一次性发完、接收端也刚好一次性读取到完整帧数据,解码显示正常
  • 屏幕内容复杂时,PNG压缩后体积变大,单个帧会被拆分成多次传输,或者多个帧的数据粘在一起,接收端拿到的不是完整的PNG数据,解码就会出现扭曲失真。

另外你原有代码里ImageToByte方法直接释放了传入的iImage(也就是类变量bmpScreenshot),也会存在潜在的对象访问异常问题。


具体修复方案

发送端修改

  1. 新增帧头规则:每次发送PNG数据前,先发送4字节的帧长度标识(用网络大端字节序),再发送实际的PNG帧数据,让接收端可以明确知道当前帧的总长度
  2. 修复原有代码的对象释放问题
  3. 可添加帧率控制避免带宽占满

修改后的发送端代码参考:

// 优化后的ImageToByte方法,不再释放传入的Image对象
private byte[] ImageToByte(Image iImage)
{
    // 用using自动管理MemoryStream生命周期,不需要全局变量
    using (MemoryStream ms = new MemoryStream()) 
    {
        iImage.Save(ms, ImageFormat.Png);
        // 可替换为JPEG格式调整压缩率,降低数据体积:iImage.Save(ms, ImageFormat.Jpeg);
        return ms.ToArray();
    }
}

// 发送逻辑修改
Socket socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socket.Connect(new IPEndPoint(IPAddress.Parse("192.168.1.109"), 8500));

while(true)
{
    try
    {
        byte[] sendData = screenToByteArray();
        if(sendData == null || sendData.Length == 0)
        {
            Thread.Sleep(10);
            continue;
        }
        // 构造4字节长度帧头,转换为网络大端字节序
        byte[] lengthHeader = BitConverter.GetBytes(sendData.Length);
        if (BitConverter.IsLittleEndian) Array.Reverse(lengthHeader);
        // 先发帧头
        socket.Send(lengthHeader, 4, SocketFlags.None);
        // 再发实际帧数据
        socket.Send(sendData, sendData.Length, SocketFlags.None);
        
        Thread.Sleep(30); // 控制在约30帧每秒,可按需调整
    }
    catch (Exception ex)
    {
        Console.WriteLine("[ERROR]sendScreen Error..{0}", ex.Message);
        socket.Dispose();
        break;
    }
 }

接收端修改

接收端按照约定先收满4字节帧头,解析出当前帧总长度后,再收满对应长度的字节,才进行PNG解码显示:

// 已建立连接的TCP socket
Socket receiveSocket = [你的接收端连接Socket];
byte[] lengthBuffer = new byte[4];

while(true)
{
    // 第一步:收满4字节长度头
    int totalRead = 0;
    while(totalRead < 4)
    {
        int currRead = receiveSocket.Receive(lengthBuffer, totalRead, 4 - totalRead, SocketFlags.None);
        if(currRead == 0)
        {
            // 连接断开,退出逻辑
            return;
        }
        totalRead += currRead;
    }
    // 转换为主机字节序得到帧长度
    if(BitConverter.IsLittleEndian) Array.Reverse(lengthBuffer);
    int frameLength = BitConverter.ToInt32(lengthBuffer, 0);
    
    // 第二步:收满完整的PNG帧数据
    byte[] frameBuffer = new byte[frameLength];
    totalRead = 0;
    while(totalRead < frameLength)
    {
        int currRead = receiveSocket.Receive(frameBuffer, totalRead, frameLength - totalRead, SocketFlags.None);
        if(currRead == 0) return;
        totalRead += currRead;
    }
    
    // 第三步:解码显示
    using(MemoryStream ms = new MemoryStream(frameBuffer))
    {
        Bitmap screenBmp = (Bitmap)Image.FromStream(ms);
        // 此处添加你的画面显示逻辑
    }
}

额外优化建议

  • 把PNG格式替换为JPEG格式,可调整压缩质量大幅降低单帧体积,减少传输延迟和粘包概率
  • 实现差帧传输,只传输和上一帧有变化的屏幕区域,进一步降低传输数据量
  • 如果需要更高的传输效率,可以考虑引入H.264等视频编码格式做屏幕流压缩

内容的提问来源于stack exchange,提问作者Oguzhan Öztürk

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.03 17:45:03