RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 814617
Accepted
MiT
MiT
Asked:2020-04-16 00:39:45 +0000 UTC2020-04-16 00:39:45 +0000 UTC 2020-04-16 00:39:45 +0000 UTC

如何尽可能优化和改进代码(RDP)?(C#)

  • 772

因此,我想制作一个带有客户端和服务器的小型 RDP(没有操纵器)。有一个用于创建、压缩、发送和接收图像的小代码。看起来一切都很好,但是即使在局域网上也有野物(尽管正常接收到一半的图像),我还没有通过网络尝试过,我认为它会是一样的。

本地主机上的工件 我没有足够的知识来尽可能优化这段代码,所以我请求你的帮助!

代码:发送 SendStream.cs

using System;
using System.Drawing;
using System.Collections.Generic;
using System.Net;
using System.Net.Sockets;
using System.IO;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Forms;

namespace Client.Sender
{
    public class SendStream
    {
        private IPEndPoint ipEndPoint;
        private UdpClient _udpClient;
        private int width;
        private int height;
        private const UInt16 UdpSize = 65507;
        private const UInt16 ControlBlockSize = 5;
        private Random random = new Random();
        private byte lastA = 0; // Последний код цепочки
        private byte lastB = 0; // Последний код цепочки
        private float FPS = 60;

        public SendStream()
        {
            // Загружаем номер порта, на которой надо встать
            using (StreamReader streamReader = new StreamReader("ip.txt"))
            {
                string ip = streamReader.ReadLine();
                int port = Convert.ToInt32(streamReader.ReadLine());

                if (ip != null) ipEndPoint = new IPEndPoint(IPAddress.Parse(ip), port);
            }

            width = Screen.PrimaryScreen.Bounds.Width;
            height = Screen.PrimaryScreen.Bounds.Height;
        }

        public async Task Run()
        {
            _udpClient = new UdpClient();
            Bitmap backGround = new Bitmap(width, height);
            Graphics graphics = Graphics.FromImage(backGround);

            while (true)
            {
                await Task.Delay((int) (1000 / FPS));
                graphics.CopyFromScreen(0, 0, 0, 0, new System.Drawing.Size(width, height)); // Получаем снимок экрана

                byte[] bytes = ConvertToByte(backGround); // Получаем изображение в виде массива байтов
                List<byte[]> data = Package(bytes); // Упаковка изображения в протокол

                foreach (var block in data)
                {
                    await _udpClient.SendAsync(block, block.Length, ipEndPoint);
                }
            }
        }

        /* 
         * Конвертируем изображение в массив байтов со сжатием
         * Jpeg - качество средние, скорость средние, потери малые (полупрозрачные, 40-60%)
         * Gif - качество плохое, скорость максимальное, потери огромные (тв, 90%)
         * Png - качество выше-среднего, скорость выше-среднего, потери средние (серый цвет, 40-60%)
         *
        */
        private byte[] ConvertToByte(Bitmap bmp)
        {
            MemoryStream memoryStream = new MemoryStream();
            bmp.Save(memoryStream, System.Drawing.Imaging.ImageFormat.Gif);
            return memoryStream.ToArray();
        }


        // Пакеты для передачи UDP
        private List<byte[]> Package(byte[] bt)
        {
            int countMsg = (int) Math.Ceiling(bt.Length / (double) UdpSize); // Количество сообщений
            List<byte[]> chain = new List<byte[]>(); // Цепочка сообщений

            if (countMsg > 65536)
                throw new Exception(
                    "Вы пытаетесь передать сообщение больше 4 ГБ - протокол не подерживает передачу свыше 4 ГБ");

            byte[] controlBlock = new byte[ControlBlockSize]; // Формируем контрольный блок
            controlBlock[0] = 1;

            byte a, b;
            do
            {
                a = (byte) random.Next(0, 256);
                b = (byte) random.Next(0, 256);
            } while (a == lastA && b == lastB);
            // Цикл нужен, чтобы случайно не совпал код сообщений из другой цепочки, 
            // в одной из параллельных вселенной этот цикл выполняется бесконечно

            controlBlock[1] = a;
            controlBlock[2] = b;
            controlBlock[3] = BitConverter.GetBytes(countMsg)[0];
            controlBlock[4] = BitConverter.GetBytes(countMsg)[1];

            chain.Add(controlBlock);

            int offset = 0;
            for (int i = 0; i < countMsg; i++)
            {
                byte[] msgBlock = new byte[UdpSize]; // Формируем блок сообщения
                msgBlock[0] = 0;
                msgBlock[1] = a;
                msgBlock[2] = b;

                msgBlock[3] = BitConverter.GetBytes(i)[0];
                msgBlock[4] = BitConverter.GetBytes(i)[1];

                int msgBlockLength;
                if (bt.Length - offset <= UdpSize)
                {
                    msgBlockLength = bt.Length - offset;
                }
                else
                {
                    msgBlockLength = UdpSize - 7; // 5 - в данном случае первые 5 байтов
                }

                msgBlock[5] = BitConverter.GetBytes(msgBlockLength)[0];
                msgBlock[6] = BitConverter.GetBytes(msgBlockLength)[1];

                if (i == countMsg - 1)
                {
                    Array.Copy(bt, offset, msgBlock, 7, bt.Length - offset);
                }
                else
                {
                    Array.Copy(bt, offset, msgBlock, 7, UdpSize - 7);
                }

                chain.Add(msgBlock);

                offset += UdpSize;
                if (offset > bt.Length)
                {
                    offset = bt.Length;
                }
            }

            return chain;
        }

        /*  Описание протокола передачи LO поверх UDP
            1 байт - контрольный пакет (1 если контрольный и 0 если не контрольный)

            Расположение байтов для контрольного пакета
            2 - 3 байт кодовый номер цепочки пакетов
            4 - 5 байт количество пакетов

            Расположение байтов для неконтрольного пакета
            2 - 3 байт кодовый номер пакета
            4 - 5 байт номер пакета
            5 - 6 байт размер пакета, а именно данных без первых 6 байт (включая нулевой байт).
         */
    }
}

代码:接受 TakeStream.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Media.Imaging;
using System.IO;

namespace Server.Receiving
{
    class TakeStream
    {
        private int TTL = 70; // Время жизни пакетов
        private List<Packet> turn = new List<Packet>(); // Очередь сообщений
        private List<Packet> controls = new List<Packet>(); // Контрольные пакеты

        public delegate void EventReady(BitmapImage img);

        public event EventReady FrameReady;

        private struct Packet
        {
            public bool isControl; // Это контрольный пакет?
            public UInt16 id; // ID цепочки
            public byte[] date; // Данные пакета
            public UInt16 count; // Количество пакетов
            public UInt16 number; // Порядковый номер пакета
            public int TTL; // Длительность жызни пакета или контрола
        }

        public TakeStream()
        {
        }

        public void addPacked(byte[] data)
        {
            Packet packet = decodeMsg(data);

            if (packet.isControl)
            {
                controls.Add(packet);
            }
            else
            {
                turn.Add(packet);

                for (int i = 0; i < controls.Count; i++)
                {
                    if (controls[i].id == packet.id && controls[i].count - 1 == packet.number)
                    {
                        MemoryStream ms = new MemoryStream(Compare(packet.id));

                        controls.RemoveAt(i);
                        i--;

                        BitmapImage bitmapImg = new BitmapImage();
                        bitmapImg.BeginInit();
                        bitmapImg.CacheOption = BitmapCacheOption.OnLoad;
                        bitmapImg.StreamSource = ms;
                        bitmapImg.EndInit();
                        bitmapImg.Freeze();

                        FrameReady(bitmapImg);
                    }
                }
            }
        }

        // Сообщение
        private Packet decodeMsg(byte[] data)
        {
            Packet packet = new Packet();
            packet.TTL = 0;
            packet.isControl = data[0] == 1;
            packet.id = BitConverter.ToUInt16(data, 1);

            if (packet.isControl)
            {
                packet.count = BitConverter.ToUInt16(data, 3);
            }
            else
            {
                packet.number = BitConverter.ToUInt16(data, 3);
                packet.date = new byte[BitConverter.ToUInt16(data, 5)];

                try
                {
                    Array.Copy(data, 7, packet.date, 0, packet.date.Length);
                }
                catch (Exception ex)
                {
                }
            }

            return packet;
        }

        // Исходные данные
        private byte[] Compare(UInt16 id)
        {
            List<byte> data = new List<byte>();

            for (int i = 0; i < turn.Count; i++)
            {
                if (turn[i].id == id)
                {
                    data.AddRange(turn[i].date);
                    turn.RemoveAt(i);
                    i--;
                }
            }

            return data.ToArray();
        }

        private void RemoveChain(UInt16 id)
        {
            for (int i = 0; i < turn.Count; i++)
            {
                if (turn[i].id == id)
                {
                    turn.RemoveAt(i);
                }
            }
        }

        /*  Описание протокола передачи LO поверх UDP
            1 байт - контрольный пакет (1 если контрольный и 0 если не контрольный)

            Расположение байтов для контрольного пакета
            2 - 3 байт кодовый номер цепочки пакетов
            4 - 5 байт количество пакетов

            Расположение байтов для неконтрольного пакета
            2 - 3 байт кодовый номер пакета
            4 - 5 байт номер пакета
            5 - 6 байт размер пакета, а именно данных без первых 6 байт (включая нулевой байт).
         */
    }
}

代码:接受具有给定大小的空 UserControlScreenStream.cs PS 表单

using System;
using System.Text;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Net;
using System.Net.Sockets;
using System.IO;
using PRP_Server.Receiving;

namespace Server.UserControls
{
    public partial class UserControlScreenStream : UserControl
    {
        private UdpClient udpClient;
        private const UInt16 UdpSize = 65507;
        private const UInt16 ControlBlockSize = 5;

        private delegate void AsynkWorker();

        private delegate void DrawEvent(BitmapImage bitmapImage);

        private delegate void Invoke(byte[] data);

        private TakeStream takeStream = new TakeStream();
        private UInt16 _port;

        public UserControlScreenStream()
        {
            InitializeComponent();
            takeStream.FrameReady += new TakeStream.EventReady(decoder_FrameReady);
            new AsynkWorker(Run).BeginInvoke(null, null);
        }

        void decoder_FrameReady(BitmapImage img)
        {
            this.Background = new ImageBrush(img);
        }

        void MainWindow_Invoke(byte[] data)
        {
            try
            {
                takeStream.addPacked(data);
            }
            catch (Exception ex)
            {
                // ignored
            }
        }

        private void Run()
        {
            using (StreamReader sr = new StreamReader("port.txt"))
            {
                _port = UInt16.Parse(sr.ReadLine());
            }

            UdpClient udp = new UdpClient(new IPEndPoint(IPAddress.Any, _port));
            IPEndPoint ep = new IPEndPoint(IPAddress.None, 0);

            while (true)
            {
                byte[] mass = udp.Receive(ref ep);
                Dispatcher.Invoke(new Invoke(MainWindow_Invoke), mass);
            }

            /* byte[] test = new byte[5];
            test[0] = 1;
            test[1] = 2;
            test[2] = 3;
            test[3] = 4;
            test[4] = 5;

            decoder.addPacked(test); */
        }
    }
}
c#
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    VladD
    2020-04-16T17:11:58Z2020-04-16T17:11:58Z

    优化代码并不是那么容易,尤其是别人的。有几种流行的方法。

    1. “凝视的方法”。

      Вы внимательно читаете код и пытаетесь угадать, где же в нём наиболее часто выполняющийся кусок, или кусок, который создаёт наибольшую нагрузку на память/процессор/и. т. п. В этом куске имеет смысл подумать, можно ли его как-нибудь улучшить. Например, накопление строки в цикле имеет смысл заменять на использование StringBuilder. Возможно, кое-где в критических по производительности частях программы придётся отказаться от использования мощных средств наподобие LINQ. После каждого проведённого изменения обязательно перепроверяйте, является ли оно реально оптимизацией, или, наоборот, лишь ухудшает дело.

      Не старайтесь улучшить всю программу, это отнимет у вас слишком много времени и не окупится вообще никак. Избегайте ухудшения читаемости программы.

      Недостаток этого метода в том, что даже опытные программисты не всегда могут «на глазок» установить реальную причину просадки производительности, и много времени зачастую тратится на бессмысленные не-оптимизации наподобие «переиспользовать переменную цикла» или «развернуть LINQ в процедурный код после ввода с клавиатуры».

    2. Алгоритмические оптимизации и выбор структур данных.

      Нередко производительность фрагмента программы можно улучшить в разы, выбрав подходящие алгоритмы и/или структуры данных. Очень часто в качестве универсальной структуры данных используется List<T>, в котором поиск и добавление может быть очень медленным. Имеет смысл задать себе вопрос: какие операции с моей структурой данных нужны, и как часто они будут выполняться? В зависимости от этого нужно подбирать подходящую структуру данных.

      Аналогично предыдущему случаю, после каждого проведённого изменения обязательно перепроверяйте, улучшает ли это реально ситуацию.

      Недостаток этого метода — он не всегда применим, т. к. в простых программах может и не быть алгоритмических проблем.

    3. Профилирование.

      Зачем гадать, где проблема, если можно просто измерить? Запуская программу с профайлером, вы видите, в каких местах происходит реальная задержка. Оптимизируя именно этот код (ускоряя выполнение или просто переструктурировав программу так, чтобы медленный код вызывался реже), вы добьётесь хорошего прироста производительности с достаточно небольшими изменениями. Профайлер позволит не пытаться проводить бессмысленные оптимизации кусков, которые в оптимизации не нуждаются.

      这种方法的缺点是您需要学习如何阅读和理解分析器的输出。分析器不会神奇地告诉您要更改源文件中的哪一行,它只会缓慢显示实际发生的情况。如果您是业余程序员,另一个缺点是好的分析器需要花钱。如果你从事商业编程,一定要购买profiler,你离不开它。

    • 2

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    是否可以在 C++ 中继承类 <---> 结构?

    • 2 个回答
  • Marko Smith

    这种神经网络架构适合文本分类吗?

    • 1 个回答
  • Marko Smith

    为什么分配的工作方式不同?

    • 3 个回答
  • Marko Smith

    控制台中的光标坐标

    • 1 个回答
  • Marko Smith

    如何在 C++ 中删除类的实例?

    • 4 个回答
  • Marko Smith

    点是否属于线段的问题

    • 2 个回答
  • Marko Smith

    json结构错误

    • 1 个回答
  • Marko Smith

    ServiceWorker 中的“获取”事件

    • 1 个回答
  • Marko Smith

    c ++控制台应用程序exe文件[重复]

    • 1 个回答
  • Marko Smith

    按多列从sql表中选择

    • 1 个回答
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Suvitruf - Andrei Apanasik 什么是空? 2020-08-21 01:48:09 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5