Форум программистов, компьютерный форум, киберфорум
Наши страницы

C# для начинающих

Войти
Регистрация
Восстановить пароль
 
 
Jesterru
3 / 2 / 2
Регистрация: 19.06.2016
Сообщений: 276
#1

Отправка данных через сокеты - C#

26.07.2017, 21:04. Просмотров 741. Ответов 23
Метки нет (Все метки)

Здравствуйте. Я сделал программу (Точнее 2 : сервер и клиент). Суть клиента - отправить скриншот экрана, сервера - принять и сохранить скриншот. С этой задачей программа справляется, но есть несколько нюансов :
1. Чем больше буффер приема данных, тем больше шанс того, что картинка будет частично (А бывает и полностью) черной.
2. Программа оочень медленная. При значении буффера 1 (Когда скриншоты не искажаются) для 1 скриншота тратится 5 секунд. А ведь если использовать 4к разрешение, то там и все 20-30 секунд уйдут.
Вот код :
Сервер
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
Socket _ClientS = _ServerS.Accept();
Console.WriteLine("Соединение установлено");
List<byte> _Data = new List<byte>();
int _Bytes = 0;
byte[] _Buffer = new byte[64];
do
{
    _Bytes = _ClientS.Receive(_Buffer);
    for (int i = 0; i < _Buffer.Length; i++)
    {
        _Data.Add(_Buffer[i]);
    }
}
while (_ClientS.Available > 0);
 
byte[] _BData = new byte[_Data.Count];
for (int i = 0; i < _Data.Count; i++)
{
    _BData[i] = _Data[i];
}

Клиент
Кликните здесь для просмотра всего текста
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
Socket _ClientS = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
Thread.Sleep(100);
_ClientS.Connect(_IpEndP);
//Console.WriteLine("Нажмите на любую кнопку для создания скриншота...");
//Console.ReadKey();
 
Bitmap _BitMap = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppRgb);
using (Graphics _Gr = Graphics.FromImage(_BitMap))
{
    _Gr.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
}
 
byte[] _Data = null;
BitmapData _BData = _BitMap.LockBits(new Rectangle(new Point(), _BitMap.Size), ImageLockMode.ReadOnly, PixelFormat.Format32bppRgb);
int _ByteCount = _BData.Stride * _BitMap.Height;
_Data = new Byte[_ByteCount];
Marshal.Copy(_BData.Scan0, _Data, 0, _ByteCount);
_BitMap.UnlockBits(_BData);
 
_ClientS.Send(_Data);
_ClientS.Shutdown(SocketShutdown.Both);
_ClientS.Close();

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

Чем вызвана первая проблема? Как ее исправить?
Как можно более рациональным способ решить проблему 2?
Буду преблагодарен всем тем, кто помог решить проблемы.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
26.07.2017, 21:04
Я подобрал для вас темы с готовыми решениями и ответами на вопрос Отправка данных через сокеты (C#):

.NET 3.x Параллельная отправка данных через прокси - C#
У меня такой вопрос. Допустим у меня есть список прокси, и список данных для отправки, который больше моего списка прокси. И вот как мне...

Отправка и получение сообщений через сокеты - C#
Для начала , вот статья с аналоговым примером. Вот метод для получения сообщений : void Listen(CancellationToken CancellToken){ ...

Сокеты и отправка данных - C#
Доброго времени суток, форумчане... У меня есть сервер на яве и клиент на C#. Они общаются, отсылают друг-другу информацию, но периодически...

Передача данных через сокеты. Унификация - C#
Такое дело: Искал как через сокеты отправить данные (файлы, строки и т.д.), но унифицированног способа передачи не нашел. Нашел как...

Сокеты(отправка байтов серверу и получение ответа) - C#
Нужна книга,в которой будет подробно описана работа с сокетами,т.е добавление байтов CUint и т.д. Очень нужна. Дайте ссылку на...

Сокеты. Отправка сообщений всем клиентам или только одному - C#
Здравствуйте, имеется вот такой сервер на C#: using System; using System.Collections.Generic; using System.Linq; using System.Text;...

23
Usaga
Эксперт .NET
3405 / 2855 / 500
Регистрация: 21.01.2016
Сообщений: 11,272
Завершенные тесты: 2
27.07.2017, 09:56 #16
Jesterru, откуда там возьмётся переполнение, если было запрошено только 32 байта? Т.е. больше 32-х байт прийти не может. И больше нигде данная переменная не меняется.
0
Jesterru
3 / 2 / 2
Регистрация: 19.06.2016
Сообщений: 276
27.07.2017, 09:58  [ТС] #17
Я бы вообще сказал, что весь этот код не очень то и рабочий (
Цитата Сообщение от Fleder Посмотреть сообщение
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
         Socket _ClientS = _ServerS.Accept();
         Console.WriteLine("Соединение установлено");
         using(MemoryStream ms = new MemoryStream())
         {
            byte[] _Buffer = new byte[64];
            int count = _ClientS.Receive(_Buffer);
            while(count > 0)
            {
               ms.Write(_Buffer, 0, count);
            }
            using(Bitmap bitmap = new Bitmap(ms))
            {
               //этот битмат - наш скриншот
            }
         }
Добавлено через 20 секунд
Usaga, Вы миниатюру посмотрите
0
Usaga
Эксперт .NET
3405 / 2855 / 500
Регистрация: 21.01.2016
Сообщений: 11,272
Завершенные тесты: 2
27.07.2017, 10:06 #18
Jesterru, конечно код не рабочий:

C#
1
2
3
4
while(count > 0)
{
    ms.Write(_Buffer, 0, count);
}
Когда данный цикл закончится, как вы думаете?)

Добавлено через 1 минуту
Правильно, когда произойдёт переполнение MemoryStream! Ведь данный цикл пишет в него одни и теже данные в ВЕЧНОМ цикле, пока оперативка не треснет...

Добавлено через 5 минут
Вот так было бы лучше:

C#
1
2
3
4
5
6
int count = _ClientS.Receive(_Buffer);
while(count > 0)
{
    ms.Write(_Buffer, 0, count);
    count = _ClientS.Receive(_Buffer);
}
1
Jesterru
3 / 2 / 2
Регистрация: 19.06.2016
Сообщений: 276
27.07.2017, 10:57  [ТС] #19
Usaga, Спасибо, заработало.
Можете подсказать, почему в этих строках выкидывает ошибку "Недопустимое значение"?
C#
1
2
3
Bitmap _BM = new Bitmap(_MS);
string _FileName = String.Format("Screen {0}.png", DateTime.Now.ToString("dd.hh.mm.ss"));
_BM.Save(_FileName);
_MS - это MemoryStrem, вы его называли "ms"
0
Usaga
Эксперт .NET
3405 / 2855 / 500
Регистрация: 21.01.2016
Сообщений: 11,272
Завершенные тесты: 2
27.07.2017, 11:08 #20
Jesterru, смею предположить, что речь про первую строку:

C#
1
Bitmap _BM = new Bitmap(_MS);
Проблема в том, что MemoryStream нужно "перемотать в начало" после того, как в него писали:

C#
1
_MS.Position = 0;
Иначе Bitmap увидит ничего, конец данных, которые даже не начинались...
0
Fleder
243 / 206 / 86
Регистрация: 09.12.2015
Сообщений: 629
27.07.2017, 11:29 #21
Jesterru, прошу прощения.
Вот рабочий вариант клиента и сервера (проверил, у меня пашет, но проверял только по "localhost").
Нажимаем на любую кнопку в клиенте - передаём скриншот на сервер.
Сервер его сохраняет в свою папку с таким названием - Screenshot_27.07.2017_58750.png (58750 - это количество секунд с начала дня).
Сервер и клиент - консольные проги (нужны ссылки на сборки System.Drawing и System.Windows.Forms).

Сервер:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
using System;
using System.IO;
using System.Net;
using System.Net.Sockets;
 
namespace Server
{
   class Program
   {
      static void Main(string[] args)
      {
         IPHostEntry ipHost = Dns.GetHostEntry("localhost");
         IPAddress ipAddr = ipHost.AddressList[0];
         IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, 11000);
 
         using(Socket sListener = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
         {
            try
            {
               sListener.Bind(ipEndPoint);
               sListener.Listen(10);
 
               while(true)
               {
                  Console.WriteLine("Ожидаем соединение через порт {0}", ipEndPoint);
                  using(Socket handler = sListener.Accept())
                  {
                     byte[] buffer = new byte[1024];
                     int count = 0;
                     using(MemoryStream ms = new MemoryStream())
                     {
                        do
                        {
                           count = handler.Receive(buffer);
                           ms.Write(buffer, 0, count);
                        }
                        while(count > 0);
                        DateTime time = DateTime.Now;
                        using(FileStream fs = new FileStream($"Screenshot_{time.ToShortDateString()}_{time.Hour * 3600 + time.Minute * 60 + time.Second}.png ", FileMode.Create))
                        {
                           ms.Position = 0;
                           ms.CopyTo(fs);
                           Console.WriteLine($"Сервер сохранил скриншот размером: {ms.Length} байт");
                        }
                     }
                     Console.WriteLine("Сервер завершил соединение с клиентом.");
                     handler.Shutdown(SocketShutdown.Both);
                  }
               }
            }
            catch(Exception ex)
            {
               Console.WriteLine(ex.ToString());
            }
            finally
            {
               Console.ReadLine();
            }
         }
      }
   }
}


Клиент:
Кликните здесь для просмотра всего текста

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Net;
using System.Net.Sockets;
using System.Windows.Forms;
 
namespace Client
{
   class Program
   {
      static void Main(string[] args)
      {
         while(true)
         {
            try
            {
               Console.WriteLine("Нажмите на любую кнопку для создания скриншота...");
               Console.ReadKey();
               SendMessageFromSocket(11000);
            }
            catch(Exception ex)
            {
               Console.WriteLine(ex.ToString());
            }
         }
      }
 
      private static void SendMessageFromSocket(int port)
      {
         IPHostEntry ipHost = Dns.GetHostEntry("localhost");
         IPAddress ipAddr = ipHost.AddressList[0];
         IPEndPoint ipEndPoint = new IPEndPoint(ipAddr, port);
 
         using(Socket socket = new Socket(ipAddr.AddressFamily, SocketType.Stream, ProtocolType.Tcp))
         {
            socket.Connect(ipEndPoint);
            using(Bitmap bmp = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppRgb))
            using(Graphics g = Graphics.FromImage(bmp))
            using(MemoryStream ms = new MemoryStream())
            {
               g.CopyFromScreen(Screen.PrimaryScreen.Bounds.X, Screen.PrimaryScreen.Bounds.Y, 0, 0, Screen.PrimaryScreen.Bounds.Size, CopyPixelOperation.SourceCopy);
               bmp.Save(ms, ImageFormat.Png);
               socket.Send(ms.ToArray());
               socket.Shutdown(SocketShutdown.Both);
               Console.WriteLine($"Клиент отправил скриншот размером: {ms.Length} байт");
            }
         }
      }
   }
}
1
Jesterru
3 / 2 / 2
Регистрация: 19.06.2016
Сообщений: 276
27.07.2017, 13:27  [ТС] #22
Цитата Сообщение от Usaga Посмотреть сообщение
cмею предположить, что речь про первую строку:
Нет, все еще вылетает исключение

Добавлено через 17 секунд
Fleder, Спасибо огромное!

Добавлено через 1 час 49 минут
Fleder, Я сейчас захотел ваш код подстроить под себя, и у меня возник вопрос : Каким образом у Вас сервер сохраняет скриншот? Там просто массив байтов помещается в мемори стрим, из него в файл стрим, а файл стрим сохраняет? Как файл стрим узнает разрешение картинки?
0
Fleder
243 / 206 / 86
Регистрация: 09.12.2015
Сообщений: 629
27.07.2017, 14:55 #23
Цитата Сообщение от Jesterru Посмотреть сообщение
Я сейчас захотел ваш код подстроить под себя, и у меня возник вопрос : Каким образом у Вас сервер сохраняет скриншот? Там просто массив байтов помещается в мемори стрим, из него в файл стрим, а файл стрим сохраняет? Как файл стрим узнает разрешение картинки?
Файловому потоку, мягко говоря, пофиг, что сохранять на диск. Он лишь сохраняет массив байт.
То есть, сохраняет массив байт и подписывает к нему расширение ".png"
А этот массив байт - это уже готовая картинка в формате png. Её мы создали на клиенте.

Отличие от вашего варианта: вы пробовали передавать сериализованные внутренности Bitmap. А это большой размер, например, 1280 х 1024 х 4 = 5242880 байт.

В этом варианте Bitmap сохраняется в картинку формата png c размером около 180 КБ (происходит компрессия)
И уже эти 180 КБ как массив байт отправляются на сервер, который их прям так и записывает на диск, добавляя лишь расширение файла.

Добавлено через 7 минут
Цитата Сообщение от Jesterru Посмотреть сообщение
Как файл стрим узнает разрешение картинки
Если же нужно именно разрешение картинки, то можно из мемори стрима воссоздать битмап и обратиться к его свойствам:
C#
1
2
3
Bitmap bmp = new Bitmap(ms);
int width = bmp.Width;
int height = bmp.Height;
Добавлено через 10 минут
Jesterru, а если вы хотели спросить, каким образом сервер узнаёт о формате картинки? То ответ - никаким. Он должен знать об этом заранее. Картинку можно передавать и в jpg и в gif форматах.
Но в png нет потерь.
1
Jesterru
3 / 2 / 2
Регистрация: 19.06.2016
Сообщений: 276
27.07.2017, 20:16  [ТС] #24
Fleder, Спасибо преогромное за все, добился того, чего хотел изначально
0
27.07.2017, 20:16
MoreAnswers
Эксперт
37091 / 29110 / 5898
Регистрация: 17.06.2006
Сообщений: 43,301
27.07.2017, 20:16
Привет! Вот еще темы с ответами:

Передача Image через Сокеты - C#
Здравствуйте коллеги. Вобщем проблема состоит в том, чтобы картинка с PictureBox на сервере передавалась в PictureBox в клиенте. Прерыл...

Передача файла по сети через сокеты - C#
Мне интересно, как сделать передачу бинарного файла по сети с помощью UDP сокетов. Реализовал функцию чтения байтов из файла. Вот она: ...

Подключение к GSM модему через сокеты - C#
Добрый день. Помогите разобраться с проблемой. Я подключаюсь к модему для того чтобы передать сообщение. Алгоритм работы такой: сообщение...

передача массива через сокеты, как? - C#
собственно сабж, пробовал много через что, но тот или ной вариант не подходит. вот к примеру NetworkStream, передает все, кроме...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
24
Ответ Создать тему
Опции темы

КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2018, vBulletin Solutions, Inc.
Рейтинг@Mail.ru