Search code examples
c#tcptcpclienttcplistener

Accept TCP Client Async


I've been making a server. I am using TcpListener.AcceptTcpClientAsync() in an async method, but I have no idea how to actually make it work. My code right now is:

private static async void StartServer()
{
    Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
    var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
    listener.Start();
    var client = await listener.AcceptTcpClientAsync();
}

How do I process the client? Do I just continue coding and it will automagically make new threads of the same method or do I need to do some magic method that will do it for me?

Edit: current code:

private static Task HandleClientAsync(TcpClient client)
{
    var stream = client.GetStream();
    // do stuff
}
/// <summary>
/// Method to be used on seperate thread.
/// </summary>
private static async void RunServerAsync()
{
    while (true)
    {
        Console.WriteLine("S: Server started on port {0}", WebVars.ServerPort);
        var listener = new TcpListener(WebVars.LocalIp, WebVars.ServerPort);
        listener.Start();
        var client = await listener.AcceptTcpClientAsync();
        await HandleClientAsync(client);
    }
}

Solution

  • Nothing will magically create dedicated threads for you, although there are some threads used for IO completion which can come into play, particularly if you don't have a synchronization context that you need to return to.

    You should decide whether you want your StartServer method to actually complete when it's accepted a single connection, or keep looping until you've been told to shut down.

    Either way, you clearly need to decide how to handle the client too. Either you could start a new thread and use synchronous methods, or you could just use asynchronous IO to handle everything in the same thread. For example, to dump the incoming data to a file:

    private Task HandleClientAsync(TcpClient client)
    {
        // Note: this uses a *synchronous* call to create the file; not ideal.
        using (var output = File.Create("client.data"))
        {
            using (var input = client.GetStream())
            {
                // Could use CopyToAsync... this is just demo code really.
    
                byte[] buffer = new byte[8192];
                int bytesRead;
                while ((bytesRead = await input.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    await output.WriteAsync(buffer, 0, bytesRead);
                }
            }
        }
    }
    

    (That's assuming the client will just terminate the connection when it's finished writing the data.) Aside from the File.Create call, this is all asynchronous - so there's no need to create a separate thread for it.

    This is just an example, of course - real connection handling would usually be more complicated. If your real handling needs anything more compute-intensive, you may well want to consider using Task.Run to use the thread pool... that way it won't interfere with accepting more connections.