问题 TcpListener:如何在等待AcceptTcpClientAsync()时停止侦听?
我不知道如何在异步方法等待传入连接时正确关闭TcpListener。
我在SO上找到了这个代码,这里是代码:
public class Server
{
private TcpListener _Server;
private bool _Active;
public Server()
{
_Server = new TcpListener(IPAddress.Any, 5555);
}
public async void StartListening()
{
_Active = true;
_Server.Start();
await AcceptConnections();
}
public void StopListening()
{
_Active = false;
_Server.Stop();
}
private async Task AcceptConnections()
{
while (_Active)
{
var client = await _Server.AcceptTcpClientAsync();
DoStuffWithClient(client);
}
}
private void DoStuffWithClient(TcpClient client)
{
// ...
}
}
主要:
static void Main(string[] args)
{
var server = new Server();
server.StartListening();
Thread.Sleep(5000);
server.StopListening();
Console.Read();
}
这条线上有一个例外
await AcceptConnections();
当我调用Server.StopListening()时,该对象被删除。
所以我的问题是,如何取消AcceptTcpClientAsync()以正确关闭TcpListener。
10502
2017-10-07 09:16
起源
答案:
虽然有一个公平的 复杂的解决方 基于a Stephen Toub的博客文章,使用内置的.NET API有更简单的解决方案:
var cancellation = new CancellationTokenSource();
await Task.Run(() => listener.AcceptTcpClientAsync(), cancellation.Token);
// somewhere in another thread
cancellation.Cancel();
此解决方案不会终止待处理的接受呼叫。但其他解决方案也没有这样做,这个解决方案至少更短。
更新: 一个更完整的示例,显示取消后应发生的情况:
var cancellation = new CancellationTokenSource();
var listener = new TcpListener(IPAddress.Any, 5555);
listener.Start();
try
{
while (true)
{
var client = await Task.Run(
() => listener.AcceptTcpClientAsync(),
cancellation.Token);
// use the client, pass CancellationToken to other blocking methods too
}
}
finally
{
listener.Stop();
}
// somewhere in another thread
cancellation.Cancel();
更新2: Task.Run
只在任务开始时检查取消令牌。要加速终止接受循环,您可能希望注册取消操作:
cancellation.Token.Register(() => listener.Stop());
5
2018-04-15 18:47
定义此扩展方法:
public static class Extensions
{
public static async Task<TcpClient> AcceptTcpClientAsync(this TcpListener listener, CancellationToken token)
{
try
{
return await listener.AcceptTcpClientAsync();
}
catch (Exception ex) when (token.IsCancellationRequested)
{
throw new OperationCanceledException("Cancellation was requested while awaiting TCP client connection.", ex);
}
}
}
在使用扩展方法接受客户端连接之前,请执行以下操作:
token.Register(() => listener.Stop());
2
2017-07-30 17:08
由于这里没有正确的工作示例,这里有一个:
假设你有两个范围 cancellationToken
和 tcpListener
,那么你可以做到以下几点:
using (cancellationToken.Register(() => tcpListener.Stop()))
{
try
{
var tcpClient = await tcpListener.AcceptTcpClientAsync();
// … carry on …
}
catch (InvalidOperationException)
{
// Either tcpListener.Start wasn't called (a bug!)
// or the CancellationToken was cancelled before
// we started accepting (giving an InvalidOperationException),
// or the CancellationToken was cancelled after
// we started accepting (giving an ObjectDisposedException).
//
// In the latter two cases we should surface the cancellation
// exception, or otherwise rethrow the original exception.
cancellationToken.ThrowIfCancellationRequested();
throw;
}
}
2
2017-11-01 06:05
为我工作:
创建一个本地虚拟客户端以连接到侦听器,并在接受连接后,不要再执行另一个异步接受(使用活动标志)。
// This is so the accept callback knows to not
_Active = false;
TcpClient dummyClient = new TcpClient();
dummyClient.Connect(m_listener.LocalEndpoint as IPEndPoint);
dummyClient.Close();
这可能是一个黑客,但它似乎比其他选项更漂亮:)
1
2018-01-11 17:00
调用 StopListening
(配置插座)是正确的。只是吞下那个特别的错误。你无法避免这种情况,因为无论如何你都需要停止待处理的呼叫。如果没有,则泄漏套接字并且挂起的异步IO和端口保持使用状态。
0
2018-01-11 17:03
在不断收听新的连接客户端时,我使用了以下解决方案:
public async Task ListenAsync(IPEndPoint endPoint, CancellationToken cancellationToken)
{
TcpListener listener = new TcpListener(endPoint);
listener.Start();
// Stop() typically makes AcceptSocketAsync() throw an ObjectDisposedException.
cancellationToken.Register(() => listener.Stop());
// Continually listen for new clients connecting.
try
{
while (true)
{
cancellationToken.ThrowIfCancellationRequested();
Socket clientSocket = await listener.AcceptSocketAsync();
}
}
catch (OperationCanceledException) { throw; }
catch (Exception) { cancellationToken.ThrowIfCancellationRequested(); }
}
- 我注册了一个回叫来打电话
Stop()
在...上 TcpListener
实例的时候 CancellationToken
被取消了。
AcceptSocketAsync
通常会立即抛出一个 ObjectDisposedException
然后。
- 我抓住了
Exception
以外 OperationCanceledException
虽然要“理智” OperationCanceledException
到外面的来电者。
我很新 async
编程,对不起,如果这个方法有问题 - 我很高兴看到它指出要从中学习!
0
2018-01-27 22:29
取消令牌有一个委托,您可以使用该委托来停止服务器。当服务器停止时,任何侦听连接调用都将引发套接字异常。
请参阅以下代码:
public class TcpListenerWrapper
{
// helper class would not be necessary if base.Active was public, c'mon Microsoft...
private class TcpListenerActive : TcpListener, IDisposable
{
public TcpListenerActive(IPEndPoint localEP) : base(localEP) {}
public TcpListenerActive(IPAddress localaddr, int port) : base(localaddr, port) {}
public void Dispose() { Stop(); }
public new bool Active => base.Active;
}
private TcpListenerActive server
public async Task StartAsync(int port, CancellationToken token)
{
if (server != null)
{
server.Stop();
}
server = new TcpListenerActive(IPAddress.Any, port);
server.Start(maxConnectionCount);
token.Register(() => server.Stop());
while (server.Active)
{
try
{
await ProcessConnection();
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
}
}
private async Task ProcessConnection()
{
using (TcpClient client = await server.AcceptTcpClientAsync())
{
// handle connection
}
}
}
0
2017-08-03 19:44