c# - 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.
}
}
解决方案
您将为每个等待的呼叫创建一个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);
}
}
这为您提供了基本方法。然后,您需要处理其他因素,例如如果相应的服务器消息没有在合理的时间内返回会发生什么,以及如果收到的服务器消息没有相应的调用,您如何记录错误。
推荐阅读
- swift - 任何人都可以帮助从 FirebaseDatabase 获得 2 级深度字典吗?
- python - 如何遍历两个字典列表以查找公共键值对?
- python-3.x - Python:如何连接两个数据帧,其中一个具有重复的键
- mongodb - Spring Data Entity UUID 在 MongoDB 中存储为 legacy UUID
- typescript - 如何从日期对象(2019 年 10 月 25 日星期五 15:27:01 GMT+0530(印度标准时间))获取“2019 年 10 月 25 日星期五”?
- javascript - 根据第一个数组重新排列数组
- json - Hadoop 从 HDFS 读取 JSON
- php - 在关系数据库中使用 UUID 列与递增 ID 和单独的 UUID 列(Laravel,Eloquent)
- sapui5 - 使用 data-sap-ui-oninit="module:..." 引导时出现参考错误
- javascript - 如何正确输入 React 输入切换值,通过`onClick`返回什么