Reputation: 3742
I created a new .NET Core worker service project. This service should also act as a TCP socket server and listen for incoming messages. So based on the code sample from the docs this is what I'm basically doing
public class Worker : BackgroundService
{
public override async Task StartAsync(CancellationToken cancellationToken)
{
TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
tcpListener.Start();
try
{
while (true)
{
TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync();
NetworkStream tcpClientStream = tcpClient.GetStream();
using StreamReader streamReader = new StreamReader(tcpClientStream);
string messageText = await streamReader.ReadToEndAsync();
// ... do things with messageText ...
}
}
catch (Exception exception)
{
// ... error handling ...
}
await base.StartAsync(cancellationToken);
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(1000, stoppingToken);
}
}
}
The ExecuteAsync
method won't get called anymore because the code is stuck in the while
loop in the StartAsync
method. When I comment out the whole overriding StartAsync
method everything works fine, the ExecuteAsync
method gets called.
Is it possible to get rid of the blocking while loop and make use of event handlers or something like that?
Upvotes: 0
Views: 990
Reputation: 13517
A BackgroundService
's typical pattern it to simply override ExecuteAsync
and wait for the stoppingToken
to signal and then exit immediately.
Take a look at the source code for BackgroundService.
If you want to use a "start/stop" pattern, then implement IHostedService
.
If you want to use BackgroundService
, then it should be laid out as so:
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
TcpListener tcpListener = new TcpListener(IPAddress.Parse("127.0.0.1"), 1234);
tcpListener.Start();
try
{
while (!stoppingToken.IsCancellationRequested)
{
TcpClient tcpClient = await tcpListener.AcceptTcpClientAsync(); // READ BELOW ABOUT THIS
NetworkStream tcpClientStream = tcpClient.GetStream();
using StreamReader streamReader = new StreamReader(tcpClientStream);
string messageText = await streamReader.ReadToEndAsync();
// ... do things with messageText ...
}
}
catch (Exception exception)
{
// ... error handling ...
}
}
Now, the biggest problem here is that AccpetTcpClientAsync
doesn't have cancellation capabilities. So, you may have to implement something like this.
So then you could do:
var tcpClient = await tcpListener.AcceptTcpClientAsync().WithCancellation(stoppingToken);
And just swallow the OperationCancelledExeption
that would be thrown in that case.
Edited To Add
Another option is that you could skip the async/await versions all together and use the Begin../End...
(BeginAcceptTcpClient()
, in this case). I tend to go down that path a lot. It's very convenient to have an event fire when something connects. Keep in mind your TcpListener
would need to be on the class scope at that point so it doesn't dispose.
Upvotes: 2