2016-03-08 96 views
1

我想在.net中使用SerialPort類。SerialPort.BaseStream.ReadAsync缺少第一個字節

我選擇保​​持我的服務異步,所以我使用SerialPort.BaseStream上的異步方法。

在我的異步方法中,我寫了一個字節[]到串口,然後開始讀取,直到我沒有收到任何更多的數據在毫秒,並返回該結果。

但是,問題是,我似乎錯過了所有答覆的第一個字節,而不是打開串口後的第一個答覆。

如果我在每次響應(讀取)後關閉端口,並在執行新請求(寫入)之前再次打開端口,則第一個字節不會丟失。但是,如果我在關閉後過早地打開端口,通常會導致"Access to the port 'COM4' is denied."異常。對於每個寫入/讀取來說,打開/關閉似乎也是非常不必要的。

這基本上就是我的方法是這樣的:

private async Task<byte[]> SendRequestAsync(byte[] request) 
{  
    // Write the request 
    await _serialPort.BaseStream.WriteAsync(request, 0, request.Length); 

    var buffer = new byte[BUFFER_SIZE]; 
    bool receiveComplete = false; 
    var bytesRead = 0; 

    // Read from the serial port 
    do 
    { 
     var responseTask = _serialPort.BaseStream.ReadAsync(buffer, bytesRead, BUFFER_SIZE - bytesRead); 
     if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask) 
     { 
      bytesRead += responseTask.Result; 
     } 
     else 
      receiveComplete = true; 


    } while (!receiveComplete); 

    var response = new byte[bytesRead]; 
    Array.Copy(buffer, 0, response, 0, bytesRead); 

    return response; 
} 

有什麼明顯的錯誤在我這樣做的方式是什麼?有沒有更聰明的方法來異步實現相同?

+0

在'WhenAny'或'WhenAll'後做'.Result'總讓我感到畏縮,我知道它永遠不應該有問題,但我仍然不喜歡它。我總是喜歡'bytesRead + = await responseTask',因爲任務完成,所以'await'沒有任何開銷,但是你得到了拋出的解包異常的優點,而不是從'.Result'獲得'AggregateException'如果你確實得到錯誤。 –

+0

您確定300 ms超時後流中沒有字節嗎?你怎麼知道你錯過了第一個字節?這是第一個字節在某種特定方面?我問的原因是,也許前面的響應留下了一些字節,所以在第一次讀取之後,每次讀取實際上都包含第一個字節,但在前一個請求的剩餘字節之後。您可能需要在每次請求前刷新流。 –

+0

我知道第一個字節缺失,因爲每個響應應該以ASCII字符'$'(NMEA消息)開始。我現在已經切換到使用同步API,但在後臺任務中運行它,它正在工作。雖然我不喜歡這個解決方案.. – Walkingsteak

回答

2

僅僅因爲你沒有觀察到最後的ReadAsync()並不意味着它被取消,它仍然在運行,這顯然是通過讀取下面消息的第一個字節來體現的。

你應該做的是通過使用CancellationToken來取消最後的ReadAsync()。請注意,超時和讀取之間可能存在競爭,但我假設如果超時已過,無法在沒有另外寫入的情況下完成讀取。

的代碼應該是這樣的:

var cts = new CancellationTokenSource(); 

do 
{ 
    var responseTask = _serialPort.BaseStream.ReadAsync(
     buffer, bytesRead, BUFFER_SIZE - bytesRead, cts.Token); 

    if (await Task.WhenAny(responseTask, Task.Delay(300)) == responseTask) 
    { 
     bytesRead += responseTask.Result; 
    } 
    else 
    { 
     cts.Cancel(); 
     receiveComplete = true; 
    } 
} while (!receiveComplete); 

注意,無論是原因和解決方法是我的猜測,這當然可能是我錯了,大約一個或兩個人。