Добрый день. Я работаю с UdpClient
и накладываю на него обертку.UdpClient.ReceiveAsync исправить раннее завершение
Для чтения у меня есть асинхронный метод:
private async Task<byte[]> Receive(UdpClient client, CancellationToken breakToken)
{
// Выход из async, если произошёл CancellationRequest
breakToken.ThrowIfCancellationRequested();
UdpReceiveResult result;
try
{
result = await client.ReceiveAsync().WithCancellation(breakToken);
}
catch(OperationCanceledException)
{
// Штатная ситуация ручной остановки Task-а
}
return result.Buffer;
}
Где WithCancellation
мое разгибание методы досрочного прекращения:
public static async Task<T> WithCancellation<T>(
this Task<T> task, CancellationToken cancellationToken)
{
var tcs = new TaskCompletionSource<bool>();
using (cancellationToken.Register(
s => ((TaskCompletionSource<bool>)s).TrySetResult(true),
tcs))
if (task != await Task.WhenAny(task, tcs.Task))
throw new OperationCanceledException(cancellationToken);
return await task;
}
И после ручной остановки чтения, когда я называю Dispose
, System.ObjectDisposedException
происходит. CallStack
:
> System.dll!System.Net.Sockets.UdpClient.EndReceive(System.IAsyncResult asyncResult, ref System.Net.IPEndPoint remoteEP) Unknown
System.dll!System.Net.Sockets.UdpClient.ReceiveAsync.AnonymousMethod__64_1(System.IAsyncResult ar) Unknown
mscorlib.dll!System.Threading.Tasks.TaskFactory<System.Net.Sockets.UdpReceiveResult>.FromAsyncCoreLogic(System.IAsyncResult iar, System.Func<System.IAsyncResult, System.Net.Sockets.UdpReceiveResult> endFunction, System.Action<System.IAsyncResult> endAction, System.Threading.Tasks.Task<System.Net.Sockets.UdpReceiveResult> promise, bool requiresSynchronization) Unknown
mscorlib.dll!System.Threading.Tasks.TaskFactory<System.Net.Sockets.UdpReceiveResult>.FromAsyncImpl.AnonymousMethod__0(System.IAsyncResult iar) Unknown
System.dll!System.Net.LazyAsyncResult.Complete(System.IntPtr userToken) Unknown
System.dll!System.Net.ContextAwareResult.CompleteCallback(object state) Unknown
mscorlib.dll!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state, bool preserveSyncCtx) Unknown
mscorlib.dll!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext, System.Threading.ContextCallback callback, object state) Unknown
System.dll!System.Net.ContextAwareResult.Complete(System.IntPtr userToken) Unknown
System.dll!System.Net.LazyAsyncResult.ProtectedInvokeCallback(object result, System.IntPtr userToken) Unknown
System.dll!System.Net.Sockets.BaseOverlappedAsyncResult.CompletionPortCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* nativeOverlapped) Unknown
mscorlib.dll!System.Threading._IOCompletionCallback.PerformIOCompletionCallback(uint errorCode, uint numBytes, System.Threading.NativeOverlapped* pOVERLAP) Unknown
Если я правильно понял, корень ошибки в ReceiveAsync
, в моем методе его остановки, чтобы быть точным. Но я не знаю, как это исправить.
Что я должен сделать, чтобы исправить эту ошибку?
Update после USR комментария:
private async Task<byte[]> Receive(UdpClient client, CancellationToken breakToken)
{
// Выход из async, если произошёл CancellationRequest
breakToken.ThrowIfCancellationRequested();
UdpReceiveResult result;
try
{
result = await client.ReceiveAsync().WithCancellation(breakToken);
}
catch(OperationCanceledException)
{
// Штатная ситуация ручной остановки Task-а
}
catch(ObjectDisposedException) { }
return result.Buffer;
}
и Dispose
ссылающееся:
public void Dispose()
{
this.cancelRecieve?.Cancel();
this.cancelRecieve?.Dispose();
try
{
this.client?.Close();
}
catch(ObjectDisposedException) { }
}
Но catch
не реагируют на ObjectDisposedException
.
Это не позволяет вызвать EndReceive во всех случаях, которые необходимы. И если вы вызываете EndReceive, вы снова получите ObjectDisposedException. Но я уже объяснил, как бороться с этим исключением. Исходный код был хорош, за исключением исключенного исключения. – usr
Почему я должен называть 'EndReceive' вручную? Мне это не нужно. И давайте вызывать «Исключение» после этого, поймать его и ничего не делать - это плохая практика. – EgoPingvina
В документации говорится, что он должен быть вызван. Это ошибка использования, чтобы не называть его. Практически это может привести к утечке памяти. Ловить исключение, чтобы справиться с ним, совершенно нормально. Нет ничего подходящего здесь. – usr