Реализовать связь UDP без блокировки пользовательского интерфейса - PullRequest
0 голосов
/ 26 сентября 2019

У меня в настоящее время частично синхронная (плохая) реализация связи UDP между моим Android-приложением и оборудованием, которое передает пакеты UDP.Приложение постоянно опрашивает оборудование на предмет информации о состоянии, которая затем используется для обновления пользовательского интерфейса.Приложение также имеет различные экраны, каждый из которых запрашивает (только когда пользователь переключает экраны, а не непрерывно) различный набор информации о конфигурации.Пользователь также может вносить изменения в конфигурации и загружать их в оборудование.Все это время обновления статуса продолжают выполняться в фоновом режиме.Я ищу решение, наиболее подходящее для моего сценария.

Вот что я сделал до сих пор (упрощен, чтобы сделать его более читабельным)

void InitializeUDP()
{
    udpClient = new UdpClient(15001);
    sender = default(IPEndPoint);
    ThreadPool.QueueUserWorkItem(o => UDP_StatusCommunicator());

    udpClient.EnableBroadcast = true;
    udpClient.Client.ReceiveTimeout = 500;
}

void UDP_StatusCommunicator()
        {
            while (true)
            {
                if (update_flag)
                {                    
                    try
                    {
                        sent_packet = FrameGenerator(frame_Queue[screen], true); //Creates UDP Packet                        
                        //CheckQuery(sent_packet);
                        udpClient.Send(sent_packet, sent_packet.Length,"192.168.4.255", 15000);
                        received_packet = udpClient.Receive(ref sender);
                        //CheckResponse(received_packet);
                        RunOnUiThread(() =>
                            {
                                Update_UI(received_packet);
                            });                        
                    }
                    catch (SocketException e)
                    {
                        Console.Writeline("Socket Timeout: " + e);
                    }
                }                
                Thread.Sleep(update_delay);
            }
        }

void UDPReadWrite(int screen, bool reading)
        {
            SelectFunctionQueue(screen);  //Select the frames according to the screen selected
            //CheckQueue(frame_Queue);

            for (int i = 0; i < frame_Queue.Length; i++)
            {
                    try
                    {
                        sent_packet = FrameGenerator(frame_Queue[i], reading);
                        //CheckQuery(sent_packet);
                        udpClient.Send(sent_packet, sent_packet.Length, "192.168.4.255", 15000);
                        received_packet = udpClient.Receive(ref sender);
                        //CheckResponse(received_packet);
                        if (sent_packet[2] == received_packet[2])  //Verify correct packet received
                        {
                            Update_UI(received_packet);                        
                        }
                        else
                        {
                            i--;                   //retry
                        }
                    }
                    catch (SocketException e)
                    {
                        Console.WriteLine("Socket Timeout: " e);
                        i--;
                    }
                }                
            }
        }

void Switch_Screen(int new_screen)
        {
            update_flag = false;
            UDPReadWrite(new_screen, true)
            update_flag = true;
        }

void User_Config_Write(int screen, byte[] data)
        {
            update_flag = false;
            Update_Payload(data);
            UDPReadWrite(screen, false)
            update_flag = true;
        }

Как вы, очевидно, заметили,это очень некорректная реализация.Я продолжаю сталкиваться с такими проблемами, как зависание пользовательского интерфейса, когда одно и то же использование сокетов выполняется двумя потоками одновременно, застревая во время ожидания пакетов.Я пытался использовать «async await», но я неправильно его реализую, что приводит к условиям гонки, а что нет.Любая помощь будет оценена

1 Ответ

0 голосов
/ 27 сентября 2019

Я не знаю, для чего предназначен этот код:

void InitializeUDP()
{
    udpClient = new UdpClient(15001);
    sender = default(IPEndPoint);
    ThreadPool.QueueUserWorkItem(o => UDP_StatusCommunicator());

    udpClient.EnableBroadcast = true;
    udpClient.Client.ReceiveTimeout = 500;
}

, но не гарантируется, что

    udpClient.EnableBroadcast = true;
    udpClient.Client.ReceiveTimeout = 500;

будет выполнено до UDP_StatusCommunicator().

Для клиентских интерфейсов, таких как Xamarin, Task.Run может быть хорошим вариантом для ThreadPool.QueueUserWorkItem.

Возможно, вы захотите взглянуть на Поток данных (Task Parallel Library) , в частностина ActionBlock для замены вашей очереди.

Вы также можете рассмотреть возможность использования Progress для сообщения об обновлениях пользовательского интерфейса или Reactive Extensions (Rx) для подписки на обновления из пользовательского интерфейса.

...