TCP отправка изображений клиенту и серверу

#c# #image #networking #tcp #client

#c# #изображение #сеть #tcp #клиент

Вопрос:

У меня есть клиентское приложение и серверное приложение. Клиентское приложение активирует эту функцию каждые 40 миллисекунд: * Обратите внимание, что nstream является экземпляром NetworkStream.

 private void SendScreen(NetworkStream nstream)
    {
        StreamWriter writer = new StreamWriter(nstream);
        ScreenCapture sc = new ScreenCapture();
        System.Drawing.Image img = sc.CaptureScreen();
        MemoryStream ms = new MemoryStream();
        img.Save(ms, System.Drawing.Imaging.ImageFormat.Bmp);
        byte[] buffer = new byte[ms.Length];
        ms.Seek(0, SeekOrigin.Begin);
        ms.Read(buffer, 0, buffer.Length);
        Console.WriteLine(buffer.Length);
        writer.WriteLine(buffer.Length);
        writer.Flush();
        nstream.Write(buffer, 0, buffer.Length);
    }
  

и это код серверного приложения для получения изображения:

 private async void ShowImage()
    {
        while (true)
        {
            string num = await reader.ReadLineAsync();
            Console.WriteLine(num);
            int ctBytes = int.Parse(num);
            byte[] buffer = new byte[ctBytes];
            await stream.ReadAsync(buffer, 0, buffer.Length);
            MemoryStream ms = new MemoryStream(buffer);
            Image img = Image.FromStream(ms);
            Bitmap bmp = new Bitmap(img);
            this.Height = bmp.Height;
            this.Width = bmp.Width;
            this.BackgroundImage = bmp;
        }
    }
  

Я не могу придумать ни одной части, которая нарушает эти действия.
На первой итерации серверного приложения все работает (хотя вместо скриншота я вижу черный экран, но количество отправленных и полученных байтов совпадает.)
На второй итерации, когда я записываю строку num, она показывает ошибку, а затем останавливается (потому что ее нельзя разобрать в int).

Комментарии:

1. Использование reader.ReadLineAsync кажется странным, поскольку это будет интерпретировать ваши данные как строку символов, которая явно не является тем, что отправляется.

2. Сначала я отправляю буфер. Длина, которая является int (преобразование в строку символов)..

3. Отправьте его как int then, а не как строку символов.

4. какая разница?

5. Я успешно воспроизвел вашу проблему. Моя установка отправляет заголовок перед каждым изображением и ожидает подтверждения. Но, как и в вашем подходе, я могу получить только первое изображение (неполное) Прямо сейчас я озадачен тем, почему это так… Я собираюсь запустить сеанс и посмотреть, что я могу придумать.

Ответ №1:

По сути, я подозреваю, что у вас проблема с синхронизацией, как и у меня, когда я попытался воспроизвести вашу проблему (успешно).

Будь то a Header или single Integer , который объявляет о предстоящей Bitmap передаче. Все это ужасно легко рассинхронизировать.

Если вы не хотите погрузиться в сетевую синхронизацию и управление потоками, я рекомендую использовать один TransmissionObject для каждой передачи между сервером и клиентом (и, возможно, другой наоборот)

Этот объект может хранить любые данные, которые должны быть переданы. (через наследование)

Наконец, при размещении объекта в сетевом потоке вы можете использовать любой согласованный Serializer

Serializer Выполняет всю работу по синхронизации информации туда, где она необходима (метаинформация, такая как длина и, наконец, содержимое)


Ну, в моем проекте repro я придумал этот класс для хранения растрового изображения.

В качестве примера того, как использовать его на сервере:

     public void Send(Bitmap bmp)
    {
        // prepare Bitmap
        BitmapTransmission bt = new BitmapTransmission(bmp);

        // try transmitting the object
        try
        {
            // lock to exclude other Threads of using the stream
            lock (m_objSendLock)
            {
                bt.Send(m_stream);                    
            }
        } 
        catch (BitmapTransmissionException ex) 
        { // will catch any exception thrown in bt.Send(...)
            Debug.Print("BitmapHeaderException: "   ex.Message);
            ///BitmapTransmissionException provides a Property `CloseConnection`
            /// it will inform you whether the Exception is recoverable
        }
    }
  

Где m_objSendLock — объект для предотвращения одновременного доступа к сетевому потоку.
m_stream подключен ли сетевой поток к клиенту.
Echo это механизм ведения журнала.

Клиент может получать такие передачи через

   public void Receive() {
         BitmapTransmission bt = bt.Receive(m_stream);
         // check for exceptions maybe?

         pictureBox1.Image = bt.Bitmap;
  }
  

Где как снова m_stream это сетевой поток, подключенный к серверу.

Если вы хотите взглянуть на (грязный, но работающий) проект repro, скажите об этом.

Комментарии:

1. Привет, я попытался использовать только BinaryFormatter (без объекта BitmapTransmission, поскольку он мне действительно не нужен — зачем мне вставлять растровое изображение в другой объект?), И он выдает следующее исключение: Двоичный поток ‘0’ не содержит допустимого двоичного заголовка. Возможные причины — недопустимый поток или изменение версии объекта между сериализацией и десериализацией.

2. Возможно, я недостаточно подчеркнул этот аспект, но причина, по которой вы хотите поместить растровое изображение в один и тот же объект, заключается в синхронизации его передачи с метаинформацией!

3. Я попробовал ваш класс, и он выдает исключение SerializationException при получении

4. Отправляете ли вы что-нибудь, кроме объекта, в сетевом потоке? Проверьте контекст, в котором я его использую здесь

5. ну да, я это сделал, но я перестаю отправлять другие вещи, когда отправляю объект.