Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/306.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# UdpClient.ReceiveAsync正确提前终止_C#_Asynchronous_Udpclient_Objectdisposedexception - Fatal编程技术网

C# UdpClient.ReceiveAsync正确提前终止

C# UdpClient.ReceiveAsync正确提前终止,c#,asynchronous,udpclient,objectdisposedexception,C#,Asynchronous,Udpclient,Objectdisposedexception,你好。我使用的是UdpClient,上面有包装器 对于阅读,我有异步方法: private async Task<byte[]> Receive(UdpClient client, CancellationToken breakToken) { // Выход из async, если произошёл CancellationRequest breakToken.ThrowIfCancellationRequested(); UdpReceiveR

你好。我使用的是
UdpClient
,上面有包装器

对于阅读,我有异步方法:

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;
}
在手动读取停止后,当我调用
Dispose
时,会发生
System.ObjectDisposedException
<代码>调用堆栈:

>   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
Dispose
调用:

public void Dispose()
{
    this.cancelRecieve?.Cancel();
    this.cancelRecieve?.Dispose();

    try
    {
        this.client?.Close();
    }
    catch(ObjectDisposedException) { }
}

但是
catch
不会对
ObjectDisposedException

做出反应。取消挂起接收的唯一方法是像您那样断开/停止/处置。这是正确的。您需要捕获并忽略该异常

对于.NET Framework来说,这是一个不幸的设计问题,因为这是实现它的唯一方法


请注意,带取消功能的
不会取消IO。接收仍在运行。这就是为什么
with cancellation
之后必须处理套接字,以确保没有更多的未决IOs。

因此,在经历了近一周的痛苦之后,我找到了原因和解决方案

首先,我查看了
UdpClient
源代码。
ReceiveAsync
方法:

[HostProtection(ExternalThreading = true)]
public Task<UdpReceiveResult> ReceiveAsync()
{
    return Task<UdpReceiveResult>.Factory.FromAsync((callback, state) => BeginReceive(callback, state), (ar)=>
        {
            IPEndPoint remoteEP = null;
            Byte[] buffer = EndReceive(ar, ref remoteEP);
            return new UdpReceiveResult(buffer, remoteEP);

        }, null);
}
新建
Dispose
实现:

/// <summary>
/// Асинхронный запрос на ожидание приёма данных с возможностью досрочного выхода
/// (для выхода из ожидания вызовите метод Disconnect())
/// </summary>
/// <param name="client">Рабочий экземпляр класса UdpClient</param>
/// <param name="breakToken">Признак досрочного завершения</param>
/// <returns>Если breakToken произошёл до вызова данного метода или в режиме ожидания
/// ответа, вернёт пустой UdpReceiveResult; при удачном получении ответа-результат
/// асинхронной операции чтения</returns>
public Task<UdpReceiveResult> ReceiveAsync(UdpClient client, CancellationToken breakToken)
    => breakToken.IsCancellationRequested
        ? Task<UdpReceiveResult>.Run(() => new UdpReceiveResult())
        : Task<UdpReceiveResult>.Factory.FromAsync(
            (callback, state) => client.BeginReceive(callback, state),
            (ar) =>
                {
                    /// Предотвращение <exception cref="ObjectDisposedException"/>
                    if (breakToken.IsCancellationRequested)
                        return new UdpReceiveResult();

                    IPEndPoint remoteEP = null;
                    var buffer = client.EndReceive(ar, ref remoteEP);
                    return new UdpReceiveResult(buffer, remoteEP);
                },
            null);
protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        this.cancelReceive?.Cancel();
        this.client?.Close();
        this.cancelReceive?.Dispose();
    }
}

我非常希望,我的决定将剥夺其他人我所经历的痛苦。

但如果我只调用
Dispose
(删除
。在
接收
中使用取消(breakToken)
),此对象将不会被处理。我听不懂ObjectDisposedException,我已经试过了…不知道你的意思。您拥有的代码很好(假设在超时的情况下,您也会终止套接字)。但是,您需要吞下ObjectDisposedException.try{result=await client.ReceiveAsync().WithCancellation(breakToken)}catch(OperationCanceledException){//并尝试{this.client?.Close();}catch(ObjectDisposedException){}未捕获此异常。是否向breakToken发送信号?在这种情况下,接收任务被丢弃。接收任务是OBJEDSPEX出现故障的任务。是的,但忘了在此处触发它)这在所有需要的情况下都无法调用EndReceive。如果调用EndReceive,您将再次收到ObjectDisposedException。但我已经解释了如何处理这个例外。您的原始代码很好,除了坏掉的异常。文档中说必须调用它。不调用它是一个使用错误。实际上,它可能会泄漏内存。捕获一个异常来处理它是完全正确的。在这里什么都不做是合适的,这不是保证。此外,它似乎并没有表明没有泄漏。而且,没有理由这样做。新的ReceiveAsync代码更加复杂。您所需要的只是catch(ODE),框架是正确的。根据文档,您误用了它。关闭是正确的,但必须始终调用End方法。答案是错误的,原因与我已经解释过的相同:不能保证是这样。答案取决于对当前代码的反编译。随时都可能中断。文档状态
异步BeginReceive操作必须通过调用EndReceive方法来完成。
/// <summary>
/// Асинхронный запрос на ожидание приёма данных с возможностью досрочного выхода
/// (для выхода из ожидания вызовите метод Disconnect())
/// </summary>
/// <param name="client">Рабочий экземпляр класса UdpClient</param>
/// <param name="breakToken">Признак досрочного завершения</param>
/// <returns>Если breakToken произошёл до вызова данного метода или в режиме ожидания
/// ответа, вернёт пустой UdpReceiveResult; при удачном получении ответа-результат
/// асинхронной операции чтения</returns>
public Task<UdpReceiveResult> ReceiveAsync(UdpClient client, CancellationToken breakToken)
    => breakToken.IsCancellationRequested
        ? Task<UdpReceiveResult>.Run(() => new UdpReceiveResult())
        : Task<UdpReceiveResult>.Factory.FromAsync(
            (callback, state) => client.BeginReceive(callback, state),
            (ar) =>
                {
                    /// Предотвращение <exception cref="ObjectDisposedException"/>
                    if (breakToken.IsCancellationRequested)
                        return new UdpReceiveResult();

                    IPEndPoint remoteEP = null;
                    var buffer = client.EndReceive(ar, ref remoteEP);
                    return new UdpReceiveResult(buffer, remoteEP);
                },
            null);
protected virtual void Dispose(bool disposing)
{
    if (disposing)
    {
        this.cancelReceive?.Cancel();
        this.client?.Close();
        this.cancelReceive?.Dispose();
    }
}