[英]TaskCompletionSource deadlock while using SocketAsyncEventArgs
我有简单的控制台 TCP 服务器应用程序,我正在尝试使其异步。 由于某种原因,我陷入僵局。 我从 PowerShell 运行Test-NetConnection localhost -Port 12345
来触发传入连接。
我有一个假设: OnAcceptCompleted
方法在操作系统产生的Thread
内部被调用,它不是ThreadPool
的一部分。 但我不知道如何解决它。
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;
class Program
{
private static CancellationTokenSource _shutdownSource = new CancellationTokenSource();
static async Task Main()
{
using var listener = new Socket(SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Any, 12345));
listener.Listen(10);
while (!_shutdownSource.IsCancellationRequested)
{
var socket = await AcceptClient(listener, _shutdownSource.Token).ConfigureAwait(false);
// execution never go this line
Console.WriteLine($"Client accepted {socket.RemoteEndPoint}");
}
}
private static async Task<Socket> AcceptClient(Socket listener, CancellationToken token)
{
var source = new TaskCompletionSource<Socket>();
var args = new SocketAsyncEventArgs();
args.UserToken = new TaskCompletionSource<Socket>();
args.Completed += OnAcceptCompleted;
if (!listener.AcceptAsync(args))
{
OnAcceptCompleted(listener, args);
}
using (token.Register(() => source.TrySetCanceled()))
{
// we don't need ConfigureAwait(false) here, but just to be sure
return await source.Task.ConfigureAwait(false);
}
}
private static void OnAcceptCompleted(object sender, SocketAsyncEventArgs args)
{
var source = (TaskCompletionSource<Socket>)args.UserToken;
if (args.SocketError == SocketError.Success)
{
// checked in debugger, TaskCompletionSource goes to RanToCompletion status here
source.TrySetResult(args.AcceptSocket);
}
else if (args.SocketError == SocketError.OperationAborted)
{
source.TrySetCanceled();
}
else
{
source.TrySetException(new InvalidOperationException("Socket error = " + args.SocketError));
}
}
}
我不明白为什么你需要底部的两种方法。 为什么这是不可接受的?
static async Task Main()
{
using var listener = new Socket(SocketType.Stream, ProtocolType.Tcp);
listener.Bind(new IPEndPoint(IPAddress.Any, 12345));
listener.Listen(10);
while (!_shutdownSource.IsCancellationRequested)
{
var socket = await listener.AcceptAsync(_shutdownSource.Token);
Console.WriteLine($"Client accepted {socket.RemoteEndPoint}");
}
}
正如您正确指出的那样,您正在使用两个不同TaskCompletionSource
对象,而它们应该是相同的。 所以source
object 从未完成。
但是,使用原始 sockets 充满了困难,您应该改用TcpListener
,它会为您处理所有这些
class Program
{
private static CancellationTokenSource _shutdownSource = new CancellationTokenSource();
static async Task Main()
{
var listener = new TcpListener(IPAddress.Any, 12345);
try
{
listener.Start(10);
while (!_shutdownSource.IsCancellationRequested)
{
var client = await listener.AcceptTcpClientAsync(_shutdownSource.Token).ConfigureAwait(false);
Console.WriteLine($"Client accepted {client.RemoteEndPoint}");
// hand off client to another function
// Task.Run(() => HandleYourClient(client, _shutdownSource.Token));
// otherwise `client` needs `using` above to dispose
}
}
catch (OperationCanceledException)
{ //
}
finally
{
if (listener.Active)
listener.Stop()
}
}
private async Task HandleClient(TcpClient client, CancellationToken token)
{
using client;
using var stream = client.GetStream();
// do stuff
}
}
对不起这是我的错。 我在AcceptClient
方法中创建 2 TaskCompletionSource<Socket>
。 固定代码:
private static async Task<Socket> AcceptClient(Socket listener, CancellationToken token)
{
var source = new TaskCompletionSource<Socket>();
var args = new SocketAsyncEventArgs();
args.UserToken = source;
args.Completed += OnAcceptCompleted;
if (!listener.AcceptAsync(args))
{
OnAcceptCompleted(listener, args);
}
using (token.Register(() => source.TrySetCanceled()))
{
return await source.Task.ConfigureAwait(false);
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.