2017-07-03 38 views
1

我有一个使用UDP与硬件设备通信的库。 谈话是这样的:使用TaskCompletionSource和异步/等待的UDP协议

|------------000E------------>| 
|        | 
|<-----------000F-------------| 
|        | 
|------------DC23------------>| 
|        | 
|<-----------DC24-------------| 

首先,我发出的操作码000E和期望响应得到000F。一旦我获得了000F,我就发出一个DC23,并期待一个DC24的回应。 (响应中包含附加信息以及操作码。)将来,可能需要将更多步骤添加到此对话中。

负责与设备通信的对象具有以下接口:

public class Communication : ICommunication 
{ 
    public Communication(); 
    public bool Send_LAN(byte subnetID, byte deviceID, int operateCode, ref byte[] addtional); 
    public event DataArrivalHandler DataArrival; 
    public delegate void DataArrivalHandler(byte subnetID, byte deviceID, int deviceType, int operateCode, int lengthOfAddtional, ref byte[] addtional); 
} 

当我尝试天真地我结束了在DataArrival事件处理程序,确实according事情的,switch statement write验证码在响应代码,就像这样:

private void _com_DataArrival(byte subnetID, byte deviceID, int deviceTypeCode, int operateCode, int lengthOfAddtional, ref byte[] addtional) 
    { 
     Debug.WriteLine($"OpCode: 0x{operateCode:X4}"); 
     switch (operateCode) 
     { 
     case 0x000F: // Response to scan 
      // Process the response... 
      _com.Send_LAN(subnet, device, 0xDC23, ...); 
      break; 
     case 0xDC24: 
      // Continue processing... 
      break; 
     } 
    } 

它开始看起来像它会变成一个状态机。我认为必须有更好的方法来使用TaskCompletionSourceasync/await

我该如何去做这件事?

+0

唐忘记了你可能会错误的udp数据报,这可能会导致你的状态机不能使用状态机 –

+0

这是我不想使用状态机的原因之一。 –

+1

这个问题不取决于你是否使用状态机。在这两种解决方案中,您都必须检查是否发生超时。我认为州议会更容易。 (当应答超时时重新传输) –

回答

1

如果你只是想知道如何在这里使用TaskCompletionSource - 例如,你可以这样做:

public Task<Response> RequestAsync(byte subnetID, byte deviceID, int deviceType, int operateCode, ref byte[] addtional, int expectedResponseCode, CancellationToken ct = default(CancellationToken)) { 
    var tcs = new TaskCompletionSource<Response>();   
    DataArrivalHandler handler = null; 
    handler = (byte sub, byte device, int type, int opCode, int length, ref byte[] additional) => { 
     // got something, check if that is what we are waiting for 
     if (opCode == expectedResponseCode) { 
      DataArrival -= handler; 
      // construct response here 
      Response res = null; // = new Response(subnetID, deviceID, etc) 
      tcs.TrySetResult(res); 
     } 
    }; 
    DataArrival += handler; 
    // you can use cancellation for timeouts also 
    ct.Register(() => 
    { 
     DataArrival -= handler; 
     tcs.TrySetCanceled(ct); 
    }); 
    if (!Send_LAN(subnetID, deviceID, operateCode, ref addtional)) { 
     DataArrival -= handler;     
     // throw here, or set exception on task completion source, or set result to null 
     tcs.TrySetException(new Exception("Send_LAN returned false")); 
    } 
    return tcs.Task; 
} 

public class Response { 
    public byte SubnetID { get; set; } 
    // etc 
} 

然后你就可以在请求 - 响应的方式使用它:

var response = await communication.RequestAsync(...); 
+0

如果一个000E请求可以获得多个000F响应,并且每个000F必须跟随其自己的DC23,您是否仍然可以使用异步/等待? –

+0

@RonInbar如果我理解正确,您可以连续发出多个请求。首先发送000E并期望000F。然后发送DC23并根据需要多次重复使用000F。 – Evk

+0

000E被发送到*广播地址*并且总线上的每个设备用其信息响应000F。然后,DC23请求必须发送到每个设备,从该设备请求更多特定信息。您的代码分离的事件处理程序时响应到达,所以它不能够处理超过一个响应单个请求。如果你不分离事件处理程序会发生什么? –

1

你可以像编写同步IO一样编写它,而且通常比基于事件的代码要容易得多。

例如,你可以说:

await SendAsync("000E"); 
var received = await ReceiveAsync(); 
if (received != "000F") AbortConnection(); 

await能够异步IO使用与同步模式。

+0

好的,但是如何在给定上面基于事件的接口的情况下实现等待SendAsync和ReceiveAsync? –

+0

有一些标准模式。这总是可能的。 https://stackoverflow.com/questions/12858501/is-it-possible-to-await-an-event-instead-of-another-async-method你只需要薄包装。所有的逻辑进入基于await的代码调用包装。为了说明这一点,您可以等待GUI中的按钮单击。 – usr