TcpClient.ConnectAsync或Socket.BeginConnect具有非阻塞超时设置

Ale*_*lex 3 .net c# sockets task-parallel-library async-await

到目前为止,我发现的所有解决方案都基于WaitOne: 如何配置套接字连接超时生成工作线程

对我来说,用阻塞线程会WaitOne破坏异步方法的目的。生成另一个线程并不会好得多(因为异步模型会努力使用尽可能少的线程)。

还有其他解决方案仍然可以让我放弃连接尝试而不会阻塞当前线程或产生另一个线程吗?

我正在开发一个由外部代码使用的库,该库不知道我的库内部发生了什么(代码只调用了我的ConnectAsync方法,其余的工作TcpClient.ConnectAsync依此类推)。外部代码可以是任何内容(Web应用程序,桌面应用程序,Windows服务等)。理想情况下,除了设置类的.Timeout属性之外,该解决方案不应要求外部代码执行任何操作来终止操作。但是,如果这是实现自定义连接超时时避免块或工作线程的唯一方法,我将不胜感激,看看我有哪些选项(就异步/等待模型而言)。

i3a*_*non 5

TcpClient.SendAsync不会收到,CancellationToken因此无法真正取消它(如何取消不可取消的异步操作?)。您可以使用WithTimeout扩展方法:

public static Task<TResult> WithTimeout<TResult>(this Task<TResult> task, TimeSpan timeout)
{
    var timeoutTask = Task.Delay(timeout).ContinueWith(_ => default(TResult), TaskContinuationOptions.ExecuteSynchronously);
    return Task.WhenAny(task, timeoutTask).Unwrap();
}
Run Code Online (Sandbox Code Playgroud)

但是,这不会取消原始操作,仅允许您的代码表现得像原来一样。除非明确处理,否则放弃的操作将永远存在。

要真正消除潜在的操作,你应该确保调用DisposeTcpClient(通过最好using的范围)。那会使废弃的任务抛出一个ObjectDisposedException(或其他)任务,因此请注意这一点。

您可以在这里查看我有关使用的答案TimeoutScope

try
{
    var client = new TcpClient();
    using (client.CreateTimeoutScope(TimeSpan.FromSeconds(2)))
    {
        var result = await client.ConnectAsync();
        // Handle result
    }
}
catch (ObjectDisposedException)
{
    return null;
}
Run Code Online (Sandbox Code Playgroud)