асинхронный / ожидающий TCP прослушиватель, сохраняющий изображения с камер

#c# #image #asynchronous #tcp #async-await

#c# #изображение #асинхронный #tcp #асинхронный-ожидающий

Вопрос:

Привет, ребята, у меня большая проблема: я создал форму Windows, которая работала как сервер, прослушивающий TCP-сообщения, отправленные некоторыми камерами, подключенными к той же частной сети. Каждая камера отправляет tcp-сообщение на сервер, когда камера что-то обнаруживает, сервер должен работать непрерывно, не замораживая интерфейс gui, и должен обрабатывать каждое TCP-сообщение. Обработка этих данных включает в себя сохранение изображения, снятого камерой отправителя. Сервер сохраняет эти изображения в папку, и здесь возникает моя проблема: сервер не может корректно сохранять каждое изображение: похоже, что некоторые байты были потеряны во время передачи, но я думаю, что все байты обогатили сервер, произошло что-то более глубокое. Может быть, это может быть способ, которым я запрограммировал сервер async / await?

У меня есть список TcpListener, потому что мне приходится использовать больше серверов, по одному для каждой вкладки.

Вот как выглядят сохраненные изображения https://imgur.com/xtlgHPk
https://imgur.com/CcvWbDH

Как вы можете видеть, не полностью сохранен, за исключением кого-то по какой-то неизвестной причине https://imgur.com/G25UPSS

     public void TcpServer(int port)
    {
        IPAddress ipAddress = null;
        string hostName = Dns.GetHostName();
        IPHostEntry ipHostInfo = Dns.GetHostEntry(hostName);
        for (int i = 0; i < ipHostInfo.AddressList.Length;   i)
        {
            if (ipHostInfo.AddressList[i].AddressFamily ==
              AddressFamily.InterNetwork)
            {
                ipAddress = ipHostInfo.AddressList[i];
                _listener.Add( new TcpListener(ipAddress, port));
                ReceiveDataAsync();
                break;
            }
        }
        if (ipAddress == null)
            throw new Exception("No IPv4 address for server");

    }

    private async void ReceiveDataAsync()
    {
        try
        {
            _listener[tbServer.SelectedIndex].Start();
            while (true)
            {
                var tcpClient = await _listener[tbServer.SelectedIndex].AcceptTcpClientAsync();
                ReadDataFromClientAsync(tcpClient);
            }
        }
        catch (Exception e)
        {
            MessageBox.Show("Errore: ", e.Message.ToString());
        }
    }

    private async Task ReadDataFromClientAsync(TcpClient client)
    {
        try
        {
            using (NetworkStream stream=client.GetStream())
            {
                int selectedTabIndex, indexOfPort;
                string endPoint;
                while (client.Connected)
                {
                    int count = 0;
                    var countBytes = new byte[4];
                    for (int i = 0; i < 6; i  )
                    {
                        count = await stream.ReadAsync(countBytes, 0, countBytes.Length);
                    }
                    //The data dimension of the TCP message is into his header, 24th byte.
                    if (count == 0)
                    {
                        break;
                    }

                    byte[] bytes = new byte[BitConverter.ToUInt32(countBytes, 0)];
                    await stream.ReadAsync(bytes, 0, bytes.Length);


                    indexOfPort = Settings.getIndexOfPort(client.Client.LocalEndPoint.ToString());
                    endPoint = client.Client.RemoteEndPoint.ToString();
                    selectedTabIndex = Settings.getIndexOfPort(client.Client.LocalEndPoint.ToString());
                    updateGui("entry", indexOfPort,endPoint);

                    BufferData bufferService = new BufferData();
                    CameraMessage cameraMessage = await bufferService.writeBufferData(bytes.ToList());
                    if (cameraMessage == null)
                        return;

                  //doing some stuff

                        msgToShow = await bufferService.SaveImageFromCamera(cameraMessage);
                      //doing other stuff  

                    }
                    client.Close();
                    closeCommunication(selectedTabIndex,indexOfPort,endPoint);
                }
            }
        }
        catch (IOException e)
        {
            updateGui("stop");
        }

    }
  

Класс BufferData:

              public class BufferData
            {
              public async Task<CameraMessage> writeBufferData(List<byte> bytesList)
          {
        CameraMessage cameraMessage = new CameraMessage();

        try
        {
            int num = 0;
            int startSubData = 0;
            for (int startData = 0; startData < bytesList.Count-8; startData = startSubData   num)
            {

                int codeDataMessage = BitConverter.ToInt32(bytesList.GetRange(startData, 4).ToArray(), 0);
                int indexDataSize = startData   4;
                int SizeDataMessage = BitConverter.ToInt32(bytesList.GetRange(indexDataSize, 4).ToArray(), 0);
                startSubData = indexDataSize   4;
                byte[] array = bytesList.GetRange(startSubData, SizeDataMessage).ToArray();
                num = 0;
                switch (codeDataMessage)
                {
                    case 14020:
                        cameraMessage.image = await this.Base64ToImage(array);
                        num = this.OffSetStringType(SizeDataMessage);
                        break;
                }
            }
            return cameraMessage;
        }
        catch (Exception ex)
        {
            Console.WriteLine(ex.Message.ToString());
            return null;
        }
    }

    public async Task<string> SaveImageFromCamera( CameraMessage cameraMessage)
    {
        string path = Settings.pathFolder   cameraMessage.ld_I_SN   @"Images";
        string res;
        if (!Directory.Exists(Settings.pathFolder cameraMessage.ld_I_SN))
        {
            Directory.CreateDirectory(Settings.pathFolder   cameraMessage.ld_I_SN   @"Images");
        }
            try
            {
            await Task.Run(() => { cameraMessage.image.Save(path   cameraMessage.ld_I_FILENAME, ImageFormat.Jpeg); });
                res = "#3 - OK - IMMAGINE SALVATA CON SUCCESSO";
                 cameraMessage.image.Dispose();
            return res;

        }
        catch (Exception ex)
            {
                res = "#3 - ERR - IMMAGINE NON SALVATA";
            return res;

        }


    }


    public async static Task<Image> Base64ToImage(byte[] imageBytes)
    {
        MemoryStream ms = new MemoryStream(imageBytes, 0, imageBytes.Length);
        ms.Write(imageBytes, 0, imageBytes.Length);
        return System.Drawing.Image.FromStream(ms, true);
    }


}
  

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

1. Итак, вы отправляете на сервер количество байтов изображения, чтобы он знал, сколько он может прочитать. вопрос 2, почему вы конвертируете его в base64 ?. также это не имеет ничего общего с asyc и, по-видимому, await

2. Почему вы считываете первые 24 байта, но сохраняете только 20-24, а затем вызываете BitConverter.ToUInt32(countBytes, 0) с этими последними 4 байтами?

3. Похоже, что вы отправляете четырехбайтовый заголовок каждому изображению, который содержит количество байтов. Эти четыре байта НЕ помещаются в cameraMessage. Но затем при обработке сообщений вы пропускаете четыре байта, что неверно.

4. Спасибо всем за ответы. Я слежу за этим заголовком TCP ibb.co/c8tJ3fD Как вы можете видеть, байты от 20 до 24 соответствуют размеру сообщения данных, поэтому я преобразую его в байт для создания моего буфера для чтения сообщения. В сообщении данных каждое передаваемое поле соответствует этой схеме ibb.co/d4L4D75

Ответ №1:

Ваша основная проблема заключается в том, что вы предполагаете, что чтение сокета будет считывать все байты, которые вы ему задаете. Это не то, как работает чтение сокетов.

При вызове await stream.ReadAsync(bytes, 0, bytes.Length) поток будет считывать любое количество байтов от 1 до bytes.Length . Он вернет количество фактически прочитанных байтов. Вы должны принять это во внимание и читать снова, пока ваш буфер не был прочитан полностью.

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

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

1. Спасибо за вашу помощь… Как я могу это сделать? Я понял, о чем вы говорите, и подумал, что проблема может быть в чем-то подобном, но я не знаю, как сообщить c # о чтении до тех пор, пока буфер.байты длины не будут фактически прочитаны

2. Операции чтения потока возвращают количество уже прочитанных байтов. Поэтому продолжайте читать, пока не прочитаете всю длину буфера, каждый раз корректируя смещение и запрошенную длину чтения на количество уже прочитанных байтов.