Передача скриншота рабочего стола с удаленного ПК по TCP - C#
Формулировка задачи:
Доброго дня всем! Пытаюсь написать для себя программу для удаленного доступа к ПК, используя протокол TCP. Застрял уже на первом этапе - пересылки скриншотов рабочего стола с удаленного ПК. Для этого клиентская часть в бесконечном цикле каждые 40 мс (25 кадров в секунду) отправляет размер пересылаемого сжатого скриншота, а потом сам скриншот:
Серверная часть принимает также в бесконечном цикле сначала размер скриншота и затем сам скриншот:
Ошибка возникает на этапе получения размера очередного скриншота (размер сжатого скриншота колеблется от 60 000 байт до 110 000 байт) - он почему-то через раз на несколько тысяч байт меньше реального. Причем, если я каждый раз пересылаю один и тот же заранее созданный скриншот, то размер всегда корректный.
Также оговорюсь, что:
1. Это лишь заготовка, поэтому подключение пока создается на localhost;
2. Предполагается множественное подключение, поэтому используется класс ClientObject (в будущем планирую список всех активных подключений держать в List'е).
Ниже прилагаю исходный код клиентской (которым будут управлять) и серверной (который будет управлять) частей.
Сервер:
Клиент:
Листинг программы
- using (FileStream fs = new FileStream("screenshot.gz", FileMode.Open)) //создаем файловый поток fs для чтения сжатого скриншота
- {
- using (BinaryReader br = new BinaryReader(fs)) //считываем в двоичный поток br из потока fs
- {
- int ReadedBytes; //количество считанных байт
- int len = (Int32)fs.Length; //размер скриншота
- byte[] length = Encoding.Unicode.GetBytes(len.ToString()); //переводим в байтовый массив размер скриншота
- stream.Write(length, 0, length.Length); //отправляем сначала размер скриншота
- do
- {
- ReadedBytes = br.Read(buf, 0, buf.Length); //считываем максимально возможно количество байт файла в массив buf и записываем количество считанных байт в переменную ReadedBytes
- stream.Write(buf, 0, ReadedBytes); //отправляем массив buf
- len -= ReadedBytes; //вычитаем из общего размера файла количество отправленных байт
- } while (len != 0); //повторяем до тех пор, пока не отправим весь файл "кусок" за "куском"
- }
- }
Листинг программы
- int ReceivedBytes = Stream.Read(ScreenLengthB, 0, ScreenLengthB.Length); //считываем из потока в массив ScreenLengthB размер очередного скриншота и записываем в переменную ReceivedBytes количество прочитанных байт
- int ScrLength = int.Parse(Encoding.Unicode.GetString(ScreenLengthB, 0, ReceivedBytes)); //преобразуем массив ScreenLengthB в число
- //далее принимаем скриншот
- ...
Листинг программы
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using System.IO;
- using System.Net;
- using System.Net.Sockets;
- using System.Runtime.Serialization.Formatters.Binary;
- using LibraryTV;
- namespace Server
- {
- public partial class ServerTV : Form
- {
- TcpListener tcpListener;
- ClientObject clientObject;
- Thread Srv;
- public ServerTV()
- {
- InitializeComponent();
- Screen.Width = this.ClientSize.Width; //устанавливаем размер pictureBox (имя Screen)
- Screen.Height = this.ClientSize.Height; //устанавливаем размер pictureBox (имя Screen)
- Screen.SizeMode = PictureBoxSizeMode.StretchImage; //устанавливаем свойство pictureBox для увеличения/уменьшения изображения в нем по мере необходимости
- Srv = new Thread(new ThreadStart(Connect)); //создаем отдельный поток для метода Connect
- Srv.Start(); //запускаем созданный поток
- }
- private void Connect()
- {
- try
- {
- tcpListener = new TcpListener(IPAddress.Any, 50000); //будем слушать 50000 порт и обрабатывать запросы с любого IP-адреса
- tcpListener.Start();
- while (true)
- {
- TcpClient client = tcpListener.AcceptTcpClient(); //получаем входящее подключение
- clientObject = new ClientObject(client, ref Screen); //создаем объект класса clientObject и передаем ему по ссылке pictureBox и наш TcpClient
- Thread clientThread = new Thread(new ThreadStart(clientObject.Process)); //создаем отдельный поток для метода Process созданного выше объекта
- clientThread.Start(); //запускаем созданный поток
- }
- }
- catch (Exception ex)
- { MessageBox.Show(ex.Message); }
- finally
- { if (tcpListener != null) tcpListener.Stop(); }
- }
- private void ServerTV_FormClosing(object sender, FormClosingEventArgs e)
- {
- tcpListener.Stop();
- Srv.Abort();
- }
- }
- public class ClientObject
- {
- public TcpClient client;
- public PrintScreen Screen;
- private PictureBox PB;
- protected internal NetworkStream Stream { get; private set; }
- public ClientObject(TcpClient tcpClient, ref PictureBox PictB) //конструктор класса
- {
- client = tcpClient;
- PB = PictB;
- Screen = new PrintScreen(); //объект класса, который будет создавать скриншоты и сжимать их
- }
- public void Process()
- {
- try
- {
- Stream = client.GetStream(); //получаем сетевой поток для чтения
- while (true) //в бесконечном цикле выполняем процедуру GetScreen получения данных
- {
- try
- { GetScreen(); }
- catch
- { MessageBox.Show("Не удалось получить скриншот экрана"); }
- }
- }
- catch (Exception ex)
- { MessageBox.Show(ex.Message); }
- finally
- {
- if (Stream != null)
- Stream.Close();
- if (client != null)
- client.Close();
- }
- }
- private void GetScreen()
- {
- try
- {
- byte[] ScreenLengthB = new byte[10]; //создаем байтовый массив, в который будем записывать размер очередного сжатого скриншота
- do
- {
- int ReceivedBytes = Stream.Read(ScreenLengthB, 0, ScreenLengthB.Length); //считываем из потока в массив ScreenLengthB размер очередного скриншота и записываем в переменную ReceivedBytes количество прочитанных байт
- int ScrLength = int.Parse(Encoding.Unicode.GetString(ScreenLengthB, 0, ReceivedBytes)); //преобразуем массив ScreenLengthB в число
- using (MemoryStream FileR = new MemoryStream()) //будем записывать сжатый скриншот сначала в память
- {
- byte[] FileScr = new byte[ScrLength]; //создаем байтовый массив по размеру скриншота, полученного ранее
- do
- {
- ReceivedBytes = Stream.Read(FileScr, 0, FileScr.Length); //считываем из потока в массив FileScr очередной "кусочек" скриншота (по возможности весь скриншот, поэтому указывается размер оставшейся части скриншота) и записываем в переменную ReceivedBytes количество прочитанных байт
- FileR.Write(FileScr, 0, ReceivedBytes); //записываем в память полученный "кусок" скриншота
- ScrLength -= ReceivedBytes; //вычитаем из размера скриншота размер принятого "куска"
- FileScr = new byte[ScrLength]; //создаем массив по новому размеру
- } while (ScrLength > 0); //выполняем всю последовательность, пока не примем весь скриншот
- using (FileStream File = new FileStream("screenshot.gz", FileMode.Create)) //создаем файл с именем screenshot.gz
- { File.Write(FileR.ToArray(), 0, FileR.ToArray().Length); } //записываем в созданный файл скриншот из памяти
- }
- Screen.Decompress(); //выполняем декомпрессию принятого сжатого скриншота
- PB.LoadAsync(@"screenshot"); //асинхронно подгружаем в pictureBox скриншот
- } while (Stream.DataAvailable); //читаем пока в потоке есть данные, доступные для чтения
- }
- catch (Exception ex)
- { MessageBox.Show(ex.Message); }
- }
- }
- }
Листинг программы
- using System;
- using System.Collections.Generic;
- using System.ComponentModel;
- using System.Data;
- using System.Drawing;
- using System.Linq;
- using System.Text;
- using System.Threading;
- using System.Threading.Tasks;
- using System.Windows.Forms;
- using System.IO;
- using System.Net.Sockets;
- using LibraryTV;
- namespace Client
- {
- public partial class Client : Form
- {
- TcpClient tcpClient;
- NetworkStream stream;
- private const string addr = "127.0.0.1";
- private const int port = 50000;
- PrintScreen screenshot;
- public Client()
- {
- InitializeComponent();
- Location = new Point(Screen.PrimaryScreen.WorkingArea.Width - this.Width, Screen.PrimaryScreen.WorkingArea.Height - this.Height); //задаем местоположение окна приложеня в правом нижнем углу экрана
- screenshot = new PrintScreen(); //создаем объект класса PrintScreen, с помощью которого будем создавать скриншоты и сжимать их
- Thread SendThread = new Thread(new ThreadStart(Connect)); //создаем отдельный поток для метода Connect
- SendThread.Start(); //запускаем созданный поток
- }
- public void Connect()
- {
- try
- {
- tcpClient = new TcpClient(addr, port); //пытаемся подключиться к серверу на заданный IP-адрес и порт
- stream = tcpClient.GetStream(); //получаем сетевой поток для записи
- SendDisplay();
- }
- catch (Exception ex)
- { MessageBox.Show(ex.Message); }
- finally
- { Disconnect(); }
- }
- public void SendDisplay()
- {
- while (true)
- {
- try
- {
- byte[] buf = new byte[55000]; //создаем байтовый массив в 55000 байт
- screenshot.makeScreenshot(); //создаем скриншот
- screenshot.Compress(); //сжимаем созданный скриншот
- using (FileStream fs = new FileStream("screenshot.gz", FileMode.Open)) //создаем файловый поток fs для чтения сжатого скриншота
- {
- using (BinaryReader br = new BinaryReader(fs)) //считываем в двоичный поток br из потока fs
- {
- int ReadedBytes; //количество считанных байт
- int len = (Int32)fs.Length; //размер скриншота
- byte[] length = Encoding.Unicode.GetBytes(len.ToString()); //переводим в байтовый массив размер скриншота
- stream.Write(length, 0, length.Length); //отправляем сначала размер скриншота
- do
- {
- ReadedBytes = br.Read(buf, 0, buf.Length); //считываем максимально возможно количество байт файла в массив buf и записываем количество считанных байт в переменную ReadedBytes
- stream.Write(buf, 0, ReadedBytes); //отправляем массив buf
- len -= ReadedBytes; //вычитаем из общего размера файла количество отправленных байт
- } while (len != 0); //повторяем до тех пор, пока не отправим весь файл "кусок" за "куском"
- }
- }
- Thread.Sleep(40); //останавливаем потом на 40 мс (25 кадров в секунду)
- }
- catch (Exception ex)
- {
- MessageBox.Show(ex.Message);
- Disconnect();
- }
- }
- }
- public void Disconnect()
- {
- if (stream != null)
- stream.Close(); //отключение потока
- if (tcpClient != null)
- tcpClient.Close(); //отключение клиента
- Environment.Exit(0); //завершение процесса
- }
- }
- }
Решение задачи: «Передача скриншота рабочего стола с удаленного ПК по TCP»
textual
Листинг программы
- using System;
- using System.Collections.Generic;
- using System.Drawing;
- using System.Drawing.Imaging;
- using System.IO;
- using System.Net.Sockets;
- using System.Threading;
- using System.Windows.Forms;
- namespace WindowsFormsApplication344
- {
- public partial class Form1 : Form
- {
- public Form1()
- {
- InitializeComponent();
- //создаем PictureBox для отображения скриншотов
- var pb = new PictureBox { Parent = this, Dock = DockStyle.Fill, SizeMode = PictureBoxSizeMode.Zoom };
- //создаем поток для клиента (на самом деле это должно запускаться на машине клиента, но для теста, клиент запускается здесь)
- ThreadPool.QueueUserWorkItem(delegate { new Client().Start(); });
- //создаем поток для сервера
- ThreadPool.QueueUserWorkItem(
- delegate
- {
- //получаем в цикле скриншоты с клиента
- foreach (var bmp in new Server().GetScreenshots())
- {
- //уничтожаем предыдущее изображение
- if (pb.Image != null) pb.Image.Dispose();
- //заносим скриншот в PictureBox
- pb.Image = bmp;
- }
- });
- }
- }
- /// <summary>
- /// Сервер
- /// </summary>
- class Server
- {
- public IEnumerable<Image> GetScreenshots(int port = 24432)
- {
- var list = new TcpListener(port);
- list.Start();
- using (var tcp = list.AcceptTcpClient())//принимаем конект
- using (var stream = tcp.GetStream())//создаем сетевой поток
- using (var br = new BinaryReader(stream)) //создаем BinaryReader
- while (true)//делаем бесконечно
- {
- //принимаем длину массива
- var len = br.ReadInt32();
- //принимаем массив
- var arr = br.ReadBytes(len);
- using(var ms = new MemoryStream(arr))//создаем временный поток для сжатого изображения
- {
- //создаем изображение
- yield return Bitmap.FromStream(ms);
- }
- }
- }
- }
- /// <summary>
- /// Клиент
- /// </summary>
- class Client
- {
- public void Start(string host = "localhost", int port = 24432)
- {
- //размеры экрана
- var rect = Screen.PrimaryScreen.Bounds;
- //конкетимся к серверу, получаем поток
- using (var tcp = new TcpClient(host, port)) //создаем TcpClient
- using (var stream = tcp.GetStream()) //получаем сетевой поток
- using (var bw = new BinaryWriter(stream)) //создаем BinaryWriter
- using (var bmp = new Bitmap(rect.Width, rect.Height)) //создаем битмап для отправки
- using (var gr = Graphics.FromImage(bmp)) //создаем канву
- using (var ms = new MemoryStream()) //создаем временный поток для сжатого изображения
- while (true) //делаем бесконечно
- {
- //захватываем изображение экрана
- gr.CopyFromScreen(rect.Left, rect.Top, 0, 0, rect.Size);
- //конвертируем изображение в массив байт в формате jpeg
- ms.Position = 0;
- bmp.Save(ms, ImageFormat.Jpeg);
- var arr = ms.ToArray(); //получаем массив байт
- //отправляем длину массива данных
- bw.Write(arr.Length);
- //отправляем массив
- bw.Write(arr);
- //точно, отправялем
- bw.Flush();
- }
- }
- }
- }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д