Передача скриншота рабочего стола с удаленного ПК по TCP - C#

Узнай цену своей работы

Формулировка задачи:

Доброго дня всем! Пытаюсь написать для себя программу для удаленного доступа к ПК, используя протокол TCP. Застрял уже на первом этапе - пересылки скриншотов рабочего стола с удаленного ПК. Для этого клиентская часть в бесконечном цикле каждые 40 мс (25 кадров в секунду) отправляет размер пересылаемого сжатого скриншота, а потом сам скриншот:
Листинг программы
  1. using (FileStream fs = new FileStream("screenshot.gz", FileMode.Open)) //создаем файловый поток fs для чтения сжатого скриншота
  2. {
  3. using (BinaryReader br = new BinaryReader(fs)) //считываем в двоичный поток br из потока fs
  4. {
  5. int ReadedBytes; //количество считанных байт
  6. int len = (Int32)fs.Length; //размер скриншота
  7. byte[] length = Encoding.Unicode.GetBytes(len.ToString()); //переводим в байтовый массив размер скриншота
  8. stream.Write(length, 0, length.Length); //отправляем сначала размер скриншота
  9. do
  10. {
  11. ReadedBytes = br.Read(buf, 0, buf.Length); //считываем максимально возможно количество байт файла в массив buf и записываем количество считанных байт в переменную ReadedBytes
  12. stream.Write(buf, 0, ReadedBytes); //отправляем массив buf
  13. len -= ReadedBytes; //вычитаем из общего размера файла количество отправленных байт
  14. } while (len != 0); //повторяем до тех пор, пока не отправим весь файл "кусок" за "куском"
  15. }
  16. }
Серверная часть принимает также в бесконечном цикле сначала размер скриншота и затем сам скриншот:
Листинг программы
  1. int ReceivedBytes = Stream.Read(ScreenLengthB, 0, ScreenLengthB.Length); //считываем из потока в массив ScreenLengthB размер очередного скриншота и записываем в переменную ReceivedBytes количество прочитанных байт
  2. int ScrLength = int.Parse(Encoding.Unicode.GetString(ScreenLengthB, 0, ReceivedBytes)); //преобразуем массив ScreenLengthB в число
  3. //далее принимаем скриншот
  4. ...
Ошибка возникает на этапе получения размера очередного скриншота (размер сжатого скриншота колеблется от 60 000 байт до 110 000 байт) - он почему-то через раз на несколько тысяч байт меньше реального. Причем, если я каждый раз пересылаю один и тот же заранее созданный скриншот, то размер всегда корректный. Также оговорюсь, что: 1. Это лишь заготовка, поэтому подключение пока создается на localhost; 2. Предполагается множественное подключение, поэтому используется класс ClientObject (в будущем планирую список всех активных подключений держать в List'е). Ниже прилагаю исходный код клиентской (которым будут управлять) и серверной (который будет управлять) частей. Сервер:
Листинг программы
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Drawing.Imaging;
  7. using System.Linq;
  8. using System.Text;
  9. using System.Threading;
  10. using System.Threading.Tasks;
  11. using System.Windows.Forms;
  12. using System.IO;
  13. using System.Net;
  14. using System.Net.Sockets;
  15. using System.Runtime.Serialization.Formatters.Binary;
  16. using LibraryTV;
  17. namespace Server
  18. {
  19. public partial class ServerTV : Form
  20. {
  21. TcpListener tcpListener;
  22. ClientObject clientObject;
  23. Thread Srv;
  24. public ServerTV()
  25. {
  26. InitializeComponent();
  27. Screen.Width = this.ClientSize.Width; //устанавливаем размер pictureBox (имя Screen)
  28. Screen.Height = this.ClientSize.Height; //устанавливаем размер pictureBox (имя Screen)
  29. Screen.SizeMode = PictureBoxSizeMode.StretchImage; //устанавливаем свойство pictureBox для увеличения/уменьшения изображения в нем по мере необходимости
  30. Srv = new Thread(new ThreadStart(Connect)); //создаем отдельный поток для метода Connect
  31. Srv.Start(); //запускаем созданный поток
  32. }
  33. private void Connect()
  34. {
  35. try
  36. {
  37. tcpListener = new TcpListener(IPAddress.Any, 50000); //будем слушать 50000 порт и обрабатывать запросы с любого IP-адреса
  38. tcpListener.Start();
  39. while (true)
  40. {
  41. TcpClient client = tcpListener.AcceptTcpClient(); //получаем входящее подключение
  42. clientObject = new ClientObject(client, ref Screen); //создаем объект класса clientObject и передаем ему по ссылке pictureBox и наш TcpClient
  43. Thread clientThread = new Thread(new ThreadStart(clientObject.Process)); //создаем отдельный поток для метода Process созданного выше объекта
  44. clientThread.Start(); //запускаем созданный поток
  45. }
  46. }
  47. catch (Exception ex)
  48. { MessageBox.Show(ex.Message); }
  49. finally
  50. { if (tcpListener != null) tcpListener.Stop(); }
  51. }
  52. private void ServerTV_FormClosing(object sender, FormClosingEventArgs e)
  53. {
  54. tcpListener.Stop();
  55. Srv.Abort();
  56. }
  57. }
  58. public class ClientObject
  59. {
  60. public TcpClient client;
  61. public PrintScreen Screen;
  62. private PictureBox PB;
  63. protected internal NetworkStream Stream { get; private set; }
  64. public ClientObject(TcpClient tcpClient, ref PictureBox PictB) //конструктор класса
  65. {
  66. client = tcpClient;
  67. PB = PictB;
  68. Screen = new PrintScreen(); //объект класса, который будет создавать скриншоты и сжимать их
  69. }
  70. public void Process()
  71. {
  72. try
  73. {
  74. Stream = client.GetStream(); //получаем сетевой поток для чтения
  75. while (true) //в бесконечном цикле выполняем процедуру GetScreen получения данных
  76. {
  77. try
  78. { GetScreen(); }
  79. catch
  80. { MessageBox.Show("Не удалось получить скриншот экрана"); }
  81. }
  82. }
  83. catch (Exception ex)
  84. { MessageBox.Show(ex.Message); }
  85. finally
  86. {
  87. if (Stream != null)
  88. Stream.Close();
  89. if (client != null)
  90. client.Close();
  91. }
  92. }
  93. private void GetScreen()
  94. {
  95. try
  96. {
  97. byte[] ScreenLengthB = new byte[10]; //создаем байтовый массив, в который будем записывать размер очередного сжатого скриншота
  98. do
  99. {
  100. int ReceivedBytes = Stream.Read(ScreenLengthB, 0, ScreenLengthB.Length); //считываем из потока в массив ScreenLengthB размер очередного скриншота и записываем в переменную ReceivedBytes количество прочитанных байт
  101. int ScrLength = int.Parse(Encoding.Unicode.GetString(ScreenLengthB, 0, ReceivedBytes)); //преобразуем массив ScreenLengthB в число
  102. using (MemoryStream FileR = new MemoryStream()) //будем записывать сжатый скриншот сначала в память
  103. {
  104. byte[] FileScr = new byte[ScrLength]; //создаем байтовый массив по размеру скриншота, полученного ранее
  105. do
  106. {
  107. ReceivedBytes = Stream.Read(FileScr, 0, FileScr.Length); //считываем из потока в массив FileScr очередной "кусочек" скриншота (по возможности весь скриншот, поэтому указывается размер оставшейся части скриншота) и записываем в переменную ReceivedBytes количество прочитанных байт
  108. FileR.Write(FileScr, 0, ReceivedBytes); //записываем в память полученный "кусок" скриншота
  109. ScrLength -= ReceivedBytes; //вычитаем из размера скриншота размер принятого "куска"
  110. FileScr = new byte[ScrLength]; //создаем массив по новому размеру
  111. } while (ScrLength > 0); //выполняем всю последовательность, пока не примем весь скриншот
  112. using (FileStream File = new FileStream("screenshot.gz", FileMode.Create)) //создаем файл с именем screenshot.gz
  113. { File.Write(FileR.ToArray(), 0, FileR.ToArray().Length); } //записываем в созданный файл скриншот из памяти
  114. }
  115. Screen.Decompress(); //выполняем декомпрессию принятого сжатого скриншота
  116. PB.LoadAsync(@"screenshot"); //асинхронно подгружаем в pictureBox скриншот
  117. } while (Stream.DataAvailable); //читаем пока в потоке есть данные, доступные для чтения
  118. }
  119. catch (Exception ex)
  120. { MessageBox.Show(ex.Message); }
  121. }
  122. }
  123. }
Клиент:
Листинг программы
  1. using System;
  2. using System.Collections.Generic;
  3. using System.ComponentModel;
  4. using System.Data;
  5. using System.Drawing;
  6. using System.Linq;
  7. using System.Text;
  8. using System.Threading;
  9. using System.Threading.Tasks;
  10. using System.Windows.Forms;
  11. using System.IO;
  12. using System.Net.Sockets;
  13. using LibraryTV;
  14. namespace Client
  15. {
  16. public partial class Client : Form
  17. {
  18. TcpClient tcpClient;
  19. NetworkStream stream;
  20. private const string addr = "127.0.0.1";
  21. private const int port = 50000;
  22. PrintScreen screenshot;
  23. public Client()
  24. {
  25. InitializeComponent();
  26. Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - this.Width, Screen.PrimaryScreen.WorkingArea.Height - this.Height); //задаем местоположение окна приложеня в правом нижнем углу экрана
  27. screenshot = new PrintScreen(); //создаем объект класса PrintScreen, с помощью которого будем создавать скриншоты и сжимать их
  28. Thread SendThread = new Thread(new ThreadStart(Connect)); //создаем отдельный поток для метода Connect
  29. SendThread.Start(); //запускаем созданный поток
  30. }
  31. public void Connect()
  32. {
  33. try
  34. {
  35. tcpClient = new TcpClient(addr, port); //пытаемся подключиться к серверу на заданный IP-адрес и порт
  36. stream = tcpClient.GetStream(); //получаем сетевой поток для записи
  37. SendDisplay();
  38. }
  39. catch (Exception ex)
  40. { MessageBox.Show(ex.Message); }
  41. finally
  42. { Disconnect(); }
  43. }
  44. public void SendDisplay()
  45. {
  46. while (true)
  47. {
  48. try
  49. {
  50. byte[] buf = new byte[55000]; //создаем байтовый массив в 55000 байт
  51. screenshot.makeScreenshot(); //создаем скриншот
  52. screenshot.Compress(); //сжимаем созданный скриншот
  53. using (FileStream fs = new FileStream("screenshot.gz", FileMode.Open)) //создаем файловый поток fs для чтения сжатого скриншота
  54. {
  55. using (BinaryReader br = new BinaryReader(fs)) //считываем в двоичный поток br из потока fs
  56. {
  57. int ReadedBytes; //количество считанных байт
  58. int len = (Int32)fs.Length; //размер скриншота
  59. byte[] length = Encoding.Unicode.GetBytes(len.ToString()); //переводим в байтовый массив размер скриншота
  60. stream.Write(length, 0, length.Length); //отправляем сначала размер скриншота
  61. do
  62. {
  63. ReadedBytes = br.Read(buf, 0, buf.Length); //считываем максимально возможно количество байт файла в массив buf и записываем количество считанных байт в переменную ReadedBytes
  64. stream.Write(buf, 0, ReadedBytes); //отправляем массив buf
  65. len -= ReadedBytes; //вычитаем из общего размера файла количество отправленных байт
  66. } while (len != 0); //повторяем до тех пор, пока не отправим весь файл "кусок" за "куском"
  67. }
  68. }
  69. Thread.Sleep(40); //останавливаем потом на 40 мс (25 кадров в секунду)
  70. }
  71. catch (Exception ex)
  72. {
  73. MessageBox.Show(ex.Message);
  74. Disconnect();
  75. }
  76. }
  77. }
  78. public void Disconnect()
  79. {
  80. if (stream != null)
  81. stream.Close(); //отключение потока
  82. if (tcpClient != null)
  83. tcpClient.Close(); //отключение клиента
  84. Environment.Exit(0); //завершение процесса
  85. }
  86. }
  87. }

Решение задачи: «Передача скриншота рабочего стола с удаленного ПК по TCP»

textual
Листинг программы
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Drawing;
  4. using System.Drawing.Imaging;
  5. using System.IO;
  6. using System.Net.Sockets;
  7. using System.Threading;
  8. using System.Windows.Forms;
  9.  
  10. namespace WindowsFormsApplication344
  11. {
  12.     public partial class Form1 : Form
  13.     {
  14.         public Form1()
  15.         {
  16.             InitializeComponent();
  17.  
  18.             //создаем PictureBox для отображения скриншотов
  19.             var pb = new PictureBox { Parent = this, Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.Zoom };
  20.  
  21.             //создаем поток для клиента (на самом деле это должно запускаться на машине клиента, но для теста, клиент запускается здесь)
  22.             ThreadPool.QueueUserWorkItem(delegate { new Client().Start(); });
  23.  
  24.             //создаем поток для сервера
  25.             ThreadPool.QueueUserWorkItem(
  26.             delegate
  27.             {
  28.                 //получаем в цикле скриншоты с клиента
  29.                 foreach (var bmp in new Server().GetScreenshots())
  30.                 {
  31.                     //уничтожаем предыдущее изображение
  32.                     if (pb.Image != null) pb.Image.Dispose();
  33.                     //заносим скриншот в PictureBox
  34.                     pb.Image = bmp;
  35.                 }
  36.             });
  37.         }
  38.     }
  39.  
  40.     /// <summary>
  41.     /// Сервер
  42.     /// </summary>
  43.     class Server
  44.     {
  45.         public IEnumerable<Image> GetScreenshots(int port = 24432)
  46.         {
  47.             var list = new TcpListener(port);
  48.             list.Start();
  49.  
  50.             using (var tcp = list.AcceptTcpClient())//принимаем конект
  51.             using (var stream = tcp.GetStream())//создаем сетевой поток
  52.             using (var br = new BinaryReader(stream)) //создаем BinaryReader
  53.             while (true)//делаем бесконечно
  54.             {
  55.                 //принимаем длину массива
  56.                 var len = br.ReadInt32();
  57.                 //принимаем массив
  58.                 var arr = br.ReadBytes(len);
  59.                 using(var ms = new MemoryStream(arr))//создаем временный поток для сжатого изображения
  60.                 {
  61.                     //создаем изображение
  62.                     yield return Bitmap.FromStream(ms);
  63.                 }
  64.             }
  65.         }
  66.     }
  67.    
  68.     /// <summary>
  69.     /// Клиент
  70.     /// </summary>
  71.     class Client
  72.     {
  73.         public void Start(string host = "localhost", int port = 24432)
  74.         {
  75.             //размеры экрана
  76.             var rect = Screen.PrimaryScreen.Bounds;
  77.  
  78.             //конкетимся к серверу, получаем поток
  79.             using (var tcp = new TcpClient(host, port)) //создаем TcpClient
  80.             using (var stream = tcp.GetStream()) //получаем сетевой поток
  81.             using (var bw = new BinaryWriter(stream)) //создаем BinaryWriter
  82.             using (var bmp = new Bitmap(rect.Width, rect.Height)) //создаем битмап для отправки
  83.             using (var gr = Graphics.FromImage(bmp)) //создаем канву
  84.             using (var ms = new MemoryStream()) //создаем временный поток для сжатого изображения
  85.             while (true) //делаем бесконечно
  86.             {
  87.                 //захватываем изображение экрана
  88.                 gr.CopyFromScreen(rect.Left, rect.Top, 0, 0, rect.Size);
  89.                 //конвертируем изображение в массив байт в формате jpeg
  90.                 ms.Position = 0;
  91.                 bmp.Save(ms, ImageFormat.Jpeg);
  92.                 var arr = ms.ToArray(); //получаем массив байт
  93.                 //отправляем длину массива данных
  94.                 bw.Write(arr.Length);
  95.                 //отправляем массив
  96.                 bw.Write(arr);
  97.                 //точно, отправялем
  98.                 bw.Flush();
  99.             }
  100.         }
  101.     }
  102. }

ИИ поможет Вам:


  • решить любую задачу по программированию
  • объяснить код
  • расставить комментарии в коде
  • и т.д
Попробуйте бесплатно

Оцени полезность:

8   голосов , оценка 4.125 из 5

Нужна аналогичная работа?

Оформи быстрый заказ и узнай стоимость

Бесплатно
Оформите заказ и авторы начнут откликаться уже через 10 минут