简体   繁体   中英

Multiple connections with TcpClient, second connection always hangs/does nothing

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 cancelled 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 correct 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 at my wits end trying to figure this out and have exhausted all my ideas.

EDIT: I moved the AcceptTcpClientAsync into the loop as suggested below and it did not change anything. 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();
}

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 you main problem, you need to put the AcceptTcpClientAsync in the loop otherwise you wont 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

Its obvious that AcceptTcpClientAsync will never get called again because you are awaiting the task. This method is what accepts the client, if you cant call it, you dont 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM