2017-07-03 40 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