0

So I have a TcpClient in a console app that is listening on port 9096. I want the client to be able to handle multiple connections (simultaneous or not). I also do not want to use Threads. I want to use async/await. I also need to be able to gracefully close the app during certain events, being careful not to lose any data. So I need a cancellation token. I have the code mostly working but there are two issues.

First, when the app starts listening and I send it data; everything works correctly as long as the sender is using the same initial connection to the app. Once a new connection (or socket I guess? not clear on the terminology) is established the app does not process the new data.

Second, when the terminate signal is given to the app and the token is canceled the app does not close. I am not getting any exceptions and I cannot figure out what I an doing wrong.

I have looked all over and cannot find an example of a TcpClient that uses async/await with a cancellation token. I also cannot find an example that I have been able to get working that correctly processes multiple connections, without using Threads or other complicated designs. I want the design as simple as possible with as little code as possible while still meeting my requirements. If using threads is the only way to do it I will, but I am soo close to getting it right I feel like I am just missing a little thing.

I am trying to figure this out at my wits end and have exhausted all my ideas.

EDIT: I moved the AcceptTcpClientAsync into the loop as suggested below and it did not change anything. The app functions the same as before.

Program.cs

class Program { private static List<Task> _listeners = new List<Task>(); private static readonly CancellationTokenSource cancelSource = new CancellationTokenSource(); static void Main(string[] args) { Console.TreatControlCAsInput = false; Console.CancelKeyPress += (o, e) => { Console.WriteLine("Shutting down."); cancelSource.Cancel(); }; Console.WriteLine("Started, press ctrl + c to terminate."); _listeners.Add(Listen(cancelSource.Token)); cancelSource.Token.WaitHandle.WaitOne(); Task.WaitAll(_listeners.ToArray(), cancelSource.Token); } } 

Listen

public async Task Listen(CancellationToken token){ var listener = new TcpListener(IPAddress.Parse("0.0.0.0"), 9096); listener.Start(); Console.WriteLine("Listening on port 9096"); while (!token.IsCancellationRequested) { // Also tried putting AcceptTcpClientAsync here. await Task.Run(async () => { var client = await listener.AcceptTcpClientAsync(); using (var stream = client.GetStream()) using (var streamReader = new StreamReader(stream, Encoding.UTF8)) using (var streamWriter = new StreamWriter(stream, Encoding.UTF8)) { while (!token.IsCancellationRequested) { // DO WORK WITH DATA RECEIVED vat data = await streamReader.ReadAsync(); await streamWriter.WriteLineAsync("Request received."); } } }); } Console.WriteLine("Stopped Accepting Requests."); listener.Server.Close(); listener.Stop(); } 
1
  • you may have to use a background-worker thread Commented Nov 24, 2018 at 1:26

1 Answer 1

1

This is actually working the way you designed it, however you have only built to receive one connection. I am not going to write a full socket implementation for you (as this can get fairly in-depth). However, as for your main problem, you need to put the AcceptTcpClientAsync in the loop otherwise you won't get any more connections:

var cancellation = new CancellationTokenSource(); ... var listener = new TcpListener(...); listener.Start(); try { while (!token.IsCancellationRequested) { var client = await listener.AcceptTcpClientAsync() ... } } finally { listener.Stop(); } // somewhere in another thread cancellation.Cancel(); 

Update

I tried that and no behavior changed. Still does not pick up any connection after the first.

 await ... while (!token.IsCancellationRequested) { // DO WORK WITH DATA RECEIVED 

It's obvious that AcceptTcpClientAsync will never get called again because you are awaiting the task. This method is what accepts the client, if you can't call it, you don't get any more clients.

You cannot block here, which is what you are doing. Please see some socket server examples to get a better idea of how to write a listener.

Sign up to request clarification or add additional context in comments.

5 Comments

I tried that and no behavior changed. Still does not pick up any connection after the first.
@JasonR i missed the most import bit, you are awaiting the task, remove the await and it will work, with the AcceptTcpClientAsync in the right place
@JasonR here is another example gist.github.com/Maxwe11/cf8cc6331ad73671846e
OMG! How simple. I cannot believe I missed that. It makes sense in hindsight and I understand everything. Just completely escaped me until you said it. Thanks.
@JasonR im glad it helped

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.