I don't know how to properly close a TcpListener while an async method await for incoming connections. I found this code on SO, here the code :
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)
{
// ...
}
}
And the Main :
static void Main(string[] args)
{
var server = new Server();
server.StartListening();
Thread.Sleep(5000);
server.StopListening();
Console.Read();
}
An exception is throwed on this line
await AcceptConnections();
when I call Server.StopListening(), the object is deleted.
So my question is, how can I cancel AcceptTcpClientAsync() for closing TcpListener properly.
You can stop Wait on Listener by killing the listener using TCP Close. You'll want to replace TCP Listen with TCP Create Listener and TCP Wait on Listener so you can fork the listener wire prior to starting to wait on it.
Probably best to use the asynchronous BeginAcceptTcpClient function. Then you can just call Stop() on the listener as it won't be blocking.
Since there's no proper working example here, here is one:
Assuming you have in scope both cancellationToken
and tcpListener
, then you can do the following:
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;
}
}
While there is a fairly complicated solution based on a blog post by Stephen Toub, there's much simpler solution using builtin .NET APIs:
var cancellation = new CancellationTokenSource();
await Task.Run(() => listener.AcceptTcpClientAsync(), cancellation.Token);
// somewhere in another thread
cancellation.Cancel();
This solution won't kill the pending accept call. But the other solutions don't do that either and this solution is at least shorter.
Update: A more complete example that shows what should happen after the cancellation is signaled:
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();
Update 2: Task.Run
only checks the cancellation token when the task starts. To speed up termination of the accept loop, you might wish to register cancellation action:
cancellation.Token.Register(() => listener.Stop());
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With