C# async/await 用于触发外部事件处理程序

C# async/await for external event handler to fire

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

您将为每个等待呼叫创建一个 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);
    }
}

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