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

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

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

Доброго дня всем! Пытаюсь написать для себя программу для удаленного доступа к ПК, используя протокол TCP. Застрял уже на первом этапе - пересылки скриншотов рабочего стола с удаленного ПК. Для этого клиентская часть в бесконечном цикле каждые 40 мс (25 кадров в секунду) отправляет размер пересылаемого сжатого скриншота, а потом сам скриншот:
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 в число
                    //далее принимаем скриншот
                    ...
Ошибка возникает на этапе получения размера очередного скриншота (размер сжатого скриншота колеблется от 60 000 байт до 110 000 байт) - он почему-то через раз на несколько тысяч байт меньше реального. Причем, если я каждый раз пересылаю один и тот же заранее созданный скриншот, то размер всегда корректный. Также оговорюсь, что: 1. Это лишь заготовка, поэтому подключение пока создается на localhost; 2. Предполагается множественное подключение, поэтому используется класс ClientObject (в будущем планирую список всех активных подключений держать в List'е). Ниже прилагаю исходный код клиентской (которым будут управлять) и серверной (который будет управлять) частей. Сервер:
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();
            }
        }
    }
}

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


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

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

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