編輯:我沒有最終根據discussion與Stephen Cleary做這種方法。如果你對我的做法感興趣,請看下面我的answer。這種方法比僅僅在Task.Run中觸發stream.Read()更好嗎?
我正在尋找一種方法來從NetworkStream
超時異步讀取。當然,問題在於無法取消NetworkStream
上的ReadAsync()
,因爲它只是忽略了CancellationToken
。我讀了一個答案,建議關閉Token取消流,但在我的情況下,這不是一個選項,因爲Tcp連接必須保持打開狀態。所以我想出了下面的代碼,但我想知道這是否比做一個
Task.Run(() => stream.Read(buffer, offset, count)
只是阻止一個線程。
public static class TcpStreamExtension
{
public static async Task<int> ReadAsyncWithTimeout(this NetworkStream stream, byte[] buffer, int offset, int count)
{
CancellationTokenSource cts = new CancellationTokenSource();
bool keepTrying = true;
Timer timer = new Timer(stream.ReadTimeout);
timer.Elapsed += new ElapsedEventHandler((sender, args) => stopTrying(sender, args, cts, out keepTrying));
timer.Start();
try
{
if (stream.CanRead)
{
while (true)
{
if (stream.DataAvailable)
{
return await stream.ReadAsync(buffer, offset, count, cts.Token).ConfigureAwait(false);
}
if (keepTrying)
{
await Task.Delay(300, cts.Token).ConfigureAwait(false);
}
else
{
cts.Dispose();
timer.Dispose();
throw new IOException();
}
}
}
}
catch (TaskCanceledException tce)
{
// do nothing
}
finally
{
cts.Dispose();
timer.Dispose();
}
if (stream.DataAvailable)
{
return await stream.ReadAsync(buffer, offset, count).ConfigureAwait(false);
}
throw new IOException();
}
private static void stopTrying(object sender, ElapsedEventArgs args, CancellationTokenSource cts, out bool keepTrying)
{
keepTrying = false;
cts.Cancel();
}
}
的應用具有潛在的能夠與幾千個端點進行通信和我想要的方式,也不會封鎖一堆線程,因爲大多數它的工作是IO來創建它。另外,超時的情況應該是
您可以在不關閉TCP連接的情況下關閉NetworkStream,如果您從TCPClient獲取流但不使用它,請獲取套接字並手動創建具有以下超載的流:'public NetworkStream(Socket socket,\t bool ownsSocket)'with 'ownSocket = false',這樣就可以使TCP連接保持打開狀態,並且可以根據需要創建儘可能多的數據流。 – Gusman
看來,如果stream.ReadAsync引發異常,cts和timer將不會被處置。 –
備註:確保在取消讀取操作後可靠地找到下一條消息的開頭,因爲您不知道蒸汽的當前狀態。 –