在不阻止UI的情况下实现UDP通信

时间:2019-09-26 12:44:49

标签: c# xamarin.android async-await udp threadpool

我目前在我的android应用和正在广播UDP数据包的硬件之间实现了UDP通信的部分同步(执行不佳)。该应用程序连续轮询硬件以获取状态信息,然后该状态信息用于更新UI。该应用程序还具有各种屏幕,每个屏幕(仅当用户切换屏幕时才请求,而不是连续的)请求一组不同的配置信息。用户还可以更改配置并将其加载到硬件。在所有这些期间,状态更新始终在后台运行。我正在寻找最适合我的情况的解决方案。

这是我到目前为止所做的(简化后使其更具可读性)

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;
        }

您会清楚地注意到,这是一个非常有缺陷的实现。我一直遇到UI冻结之类的问题,两个线程同时尝试使用相同的套接字,在等待数据包时卡住了。我尝试使用“异步等待”,但是我没有正确实现它,导致出现竞争状况,而其他情况则没有。任何帮助将不胜感激

更新:经过一些研究和测试,我发现以下各项令人满意地工作。但是,如果有人可以验证它是否正确完成,我将不胜感激

UdpClient udpClient = new UdpClient();
UdpClient r_UdpClient = new UdpClient(15001);
IPEndPoint sender = default(IPEndPoint);
ManualResetEventSlim receive = new ManualResetEventSlim(true);
Task.Run(() => UDP_Transmit());

async void UDP_Transmit()
        {
            byte[] frame;
            SelectFrameQueue(selector);
            udpClient = new UdpClient(15001);
            udpClient.EnableBroadcast = true;
            udpClient.BeginReceive(new AsyncCallback(UDP_Receive), udpClient);
            while (true)
            {
                for (int i = 0; i < frame_Queue.Length; i++)
                {                    
                        frame = FrameGenerator(frame_Queue[i]);  //Generates Frames
                        try
                        {
                            udpClient.Send(frame, frame.Length, "192.168.4.255", 15000);
                        }
                        catch (SocketException)
                        {
                            Log.Debug("Error", "Socket Exception");
                        }

                        if(!receive.Wait(10000))   //Receive Timeout
                        {
                            RunOnUiThread(() =>
                            {
                                ShowToast("Connection Timeout. Please check device");
                            });
                        };
                        await Task.Delay(update_delay);    //To release pressure from H/W
                        receive.Reset();                        
                }                             
            }
        }

void UDP_Receive(IAsyncResult result)
        {
            receive.Set();
            r_UdpClient = result.AsyncState as UdpClient;
            data = r_UdpClient.EndReceive(result, ref sender);
            RunOnUiThread(() =>
            {               
                Update_UI(data);                            
            });
            r_UdpClient.BeginReceive(new AsyncCallback(UDP_Receive), r_UdpClient);            
        }

1 个答案:

答案 0 :(得分:0)

我不知道这段代码的意图是什么

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可能是一个不错的选择。

您可能想看看Dataflow (Task Parallel Library),尤其是ActionBlock,以取代您的队列。

您可能还想考虑使用Progress向UI报告更新,或使用Reactive Extensions (Rx)从UI订阅更新。