首页 > 解决方案 > C#异步/等待外部事件处理程序触发

问题描述

我正在构建一个 API 来通过 UDP 与 Sony PTZ 摄像机进行通信。为了“查询”设备(从摄像头获取数据),您必须发送特定的 UDP 数据包,然后等待响应返回。我有一个 MessageRecieved 处理程序,它响应来自我的相机的任何传入 UDP 数据包。我知道它的方式是“当前”响应,因为相机发回的序列号与我在请求中发送的序列号相同。我试图弄清楚如何以异步/等待方式执行此操作,以便我可以创建一个单一的方法,例如GetCurrentAperatureValue

我考虑过使用并发包之类的东西来存储我发送的每个命令和序列号,然后等待服务器以相同的序列号响应,然后在包中进行查找。也许轮询袋子是否存在价值?但这对我来说感觉不对。

这是一些简短的代码来演示我正在尝试做的事情。

public class SonyAPI {

        public SonyAPI() {
            server = new UDPServer();
            server.MessageReceived += Server_MessageReceived;
            server.Start();
            sequenceNum = 0;
        }

        public async Task<AperatureValue> void GetCurrentAperatureValue(){
             //build the buffer here and send payload;
             server.send("192.168.1.28", 5321, buf);
             
             //STUCK HERE: somehow I need to wait for the MessageRecieved event handler (Below) to fire with my same sequence number that I just sent and then return the result after I process it here. 
             //Becuase this is UDP, there can be lots of messages coming in together. I need to filter out this one that I need. All of this happens in less than 2 ms.
        }


        private void Server_MessageReceived(object sender, UDPMessageEventArgs e) {
              
            var newSequenceNum = BitConverter.ToInt32(e.sequenceNum);                
            Console.WriteLine("message received" + newSequenceNum + " "+ e.RemoteEndPoint);

             //TODO: When the sequence number from the above method call comes back in, send it to the method above so it can return its value.
        }
}

标签: c#async-awaitevent-handling

解决方案


您将为每个等待的呼叫创建一个TaskCompletionSource,然后将其存储TaskCompletionSource在查找中。

private readonly ConcurrentDictionary<long, TaskCompletionSource<AperatureValue>> _taskLookup = new ConcurrentDictionary<long, TaskCompletionSource<AperatureValue>>();

public Task<AperatureValue> GetCurrentAperatureValue()
{
    long id = GenerateMessageId();

    var taskCompletionSource = new TaskCompletionSource<AperatureValue>();
    _taskLookup.TryAdd(id, taskCompletionSource);

    //build the buffer here and send payload;
    server.send("192.168.1.28", id, buf);

    return taskCompletionSource.Task;             
}

private void Server_MessageReceived(object sender, UDPMessageEventArgs e) 
{
    var newSequenceNum = BitConverter.ToInt32(e.sequenceNum);               
    if (this._taskLookup.TryRemove(
           newSequenceNum, 
           out TaskCompletionSource<AperatureValue> taskCompletionSource
         )
     )
    {
        taskCompletionSource.SetResult(e.Value);
    }
}

这为您提供了基本方法。然后,您需要处理其他因素,例如如果相应的服务器消息没有在合理的时间内返回会发生什么,以及如果收到的服务器消息没有相应的调用,您如何记录错误。


推荐阅读