2013-03-07 11 views
10

Sto cercando di utilizzare NetworkStream.ReadAsync() per leggere i dati ma non riesco a trovare come annullare ReadAsync() una volta chiamato. Per lo sfondo, NetworkStream mi viene fornito da un oggetto BluetoothClient connesso (dalla libreria Bluetooth 32Feet.NET).Come annullare NetworkStream.ReadAsync senza chiudere il flusso

L'attuale codice get-it-working che sto cercando è riportato di seguito.

int bytesRead; 

while (this.continueReading) 
{ 
    bytesRead = await this.stream.ReadAsync(this.buffer, 0, (int)this.buffer.Length); 

    Console.WriteLine("Received {0} bytes", bytesRead); 
} 

Console.WriteLine("Receive loop has ended"); 

Il codice funziona dati ricezione sottili, e si arresta loop se il flag continueReading è impostato su false e vengono ricevuti i dati, ma fino alla ricezione dati non procederà oltre la linea ReadAsync(). Non riesco a vedere come interrompere la chiamata senza ricevere dati.

Sono consapevole che v'è un sovraccarico di ReadAsync che fornisce un CancellationToken, ma sembra che come NetworkStream non ignorare il comportamento predefinito ReadAsync, il token viene ignorato (vedi NetworkStream.ReadAsync with a cancellation token never cancels).

Ho provato a chiudere il flusso sottostante e questo causa la chiamata ReadAsync in attesa per il lancio di ObjectDisposedException e anche la connessione Bluetooth sottostante viene chiusa. Idealmente non voglio interrompere completamente la connessione con il dispositivo solo per interrompere la lettura. Non sembra un modo pulito di farlo, e sembra inutile abbattere l'intero flusso solo per interrompere la chiamata a ReadAsync().

Qualche consiglio?

risposta

1

È possibile implementare un wrapper asincrono su NetworkStream.Read (o ReadAsync), che riceve anche un token di annullamento che è possibile monitorare e onorare. Qualcosa di simile a questo:

Task MyCancelableNetworkStreamReadAsync(NetworkStream stream, CancellationToken ct) 
{ 
... 
if(this.stream.CanRead) 
{ 
    do 
    { 
    //check ct.IsCancellationRequested and act as needed 
    bytesRead = await this.stream.ReadAsync(this.buffer, 0, (int)this.buffer.Length); 
    } 
    while(myNetworkStream.DataAvailable); 
} 

Si prega di notare che sto solo cercando di illustrare l'idea e si potrebbe Wnt di prendere in considerazione il ritorno Task<TResult>, così come se avere il fai {} while, qualsiasi trattamento o la pulizia supplementare, ecc. - tutto in base alle proprie esigenze.

Vorrei anche richiamare la vostra attenzione sull'articolo di Stephen Toub How do I cancel non-cancelable async operations? e sull'estensione WithCancellation che crea lì.

+0

Avevo dimenticato il controllo DataAvailable! Grazie per il codice di esempio e per il collegamento all'articolo di Stephen Toub. Entrambi saranno utili per me per ottenere ciò che cerco. Grazie! – Swampie

8

Non è possibile annullare ReadAsync poiché la chiamata interna non è gestita e utilizza le porte di IOCompletion. Le opzioni sono le seguenti.

  1. Utilizzare Socket.Shutdown(). Ciò restituirà ReadAsync con un errore di socket di OperationAborted.
  2. Attendere il timeout della lettura.
  3. Verificare se i dati sono disponibili prima di leggere dalla presa.
+0

Sfortunatamente non sto usando Socket, quindi non credo di poter usare Socket.Shutdown. Creo un client Bluetooth (da 32Feet.NET) e chiamo BluetoothClient.Connect(). Una volta connesso, richiamo il flusso tramite BluetoothClient.GetStream(). Per quanto riguarda il timeout, MSDN dice che il timeout del flusso si applica solo alla lettura sincrona() - confermata dall'impostazione della proprietà ReadTimeout e ReadAsync() non è mai scaduto.Il fatto di controllare i dati disponibili dal socket è comunque utile e qualcosa di cui avevo completamente dimenticato. Grazie – Swampie

+0

La mia comprensione della lettura mi ha deluso. Sono lieto di sapere che la mia risposta non correlata ha ancora aiutato. –

+0

Se si utilizza TCPClient, è possibile utilizzare il metodo TCPClient.Close() per generare un'eccezione su una chiamata sospesa readAsync(). –

2

sto gestendo la cancellazione con il compito di attendere per l'annullamento di chiamata di token tra la chiamata asincrona e la dichiarazione attendere in questo modo:

try { 

.... 

Task<int> readTask = input.ReadAsync(buffer, 0, buffer.Length); 
readTask.Wait(myCancellationToken); 
int readBytes= await readTask; 

.... 

} 
catch (OperationCanceledException e) 
{ 
    // handle cancellation 
}