Передача скриншота рабочего стола с удаленного ПК по 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(); } } } }
ИИ поможет Вам:
- решить любую задачу по программированию
- объяснить код
- расставить комментарии в коде
- и т.д