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);
}
}
这为您提供了基本方法。然后您将需要处理其他因素,例如如果相应的服务器消息没有在合理的时间内返回会发生什么,以及如果收到的服务器消息没有相应的调用如何记录错误。
我正在构建一个 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);
}
}
这为您提供了基本方法。然后您将需要处理其他因素,例如如果相应的服务器消息没有在合理的时间内返回会发生什么,以及如果收到的服务器消息没有相应的调用如何记录错误。