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



我目前在我的安卓应用程序和广播 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);            
}

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

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 这样的客户端 UI,Task.Run可能是一个不错的选择,而不是ThreadPool.QueueUserWorkItem

您可能需要查看数据流(任务并行库(,特别是用于替换队列的操作块。

你可能还需要考虑使用进度向 UI 报告更新,或使用反应式扩展 (Rx( 从 UI 订阅更新。

相关内容

最新更新