async / await or Begin / End with TcpListener?
Asked Answered
A

3

6

I've started to build a tcp server which will be able to accept many clients, and receive simultaneously from all of the clients new data.

Until now, I used IOCP for tcp servers which was pretty easy and comfortable, but this time I want to use the Async / Await tech. that was released in C# 5.0.

The problem is that when I started to write the server using async / await, I figured out that in tcp multiple users server use case, async / await tech. and the regular synchrony methods will work the same.

Here's a simple example to be more specific:

class Server
{
    private TcpListener _tcpListener;
    private List<TcpClient> _clients;
    private bool IsStarted;

    public Server(int port)
    {
        _tcpListener = new TcpListener(new IPEndPoint(IPAddress.Any, port));
        _clients = new List<TcpClient>();
        IsStarted = false;
    }

    public void Start()
    {
        IsStarted = true;
        _tcpListener.Start();
        Task.Run(() => StartAcceptClientsAsync());
    }

    public void Stop()
    {
        IsStarted = false;
        _tcpListener.Stop();
    }

    private async Task StartAcceptClientsAsync()
    {
        while (IsStarted)
        {
            // ******** Note 1 ********
            var acceptedClient = await _tcpListener.AcceptTcpClientAsync();

            _clients.Add(acceptedClient);
            IPEndPoint ipEndPoint = (IPEndPoint) acceptedClient.Client.RemoteEndPoint;
            Console.WriteLine("Accepted new client! IP: {0} Port: {1}", ipEndPoint.Address, ipEndPoint.Port);

            Task.Run(() => StartReadingDataFromClient(acceptedClient));
        }
    }

    private async void StartReadingDataFromClient(TcpClient acceptedClient)
    {
        try
        {
            IPEndPoint ipEndPoint = (IPEndPoint) acceptedClient.Client.RemoteEndPoint;
            while (true)
            {
                MemoryStream bufferStream = new MemoryStream();
                // ******** Note 2 ********
                byte[] buffer = new byte[1024];
                int packetSize = await acceptedClient.GetStream().ReadAsync(buffer, 0, buffer.Length);

                if (packetSize == 0)
                {
                    break;
                }

                Console.WriteLine("Accepted new message from: IP: {0} Port: {1}\nMessage: {2}",
                    ipEndPoint.Address, ipEndPoint.Port, Encoding.Default.GetString(buffer));
            }
        }
        catch (Exception)
        { 
        }
        finally
        {
            acceptedClient.Close();
            _clients.Remove(acceptedClient);
        }
    }
}

Now if you see the lines under 'Note 1' and 'Note 2', It can easily be changed to:

Note 1 from

var acceptedClient = await _tcpListener.AcceptTcpClientAsync();

to

var acceptedClient = _tcpListener.AcceptTcpClient();

And Note 2 from

int packetSize = await acceptedClient.GetStream().ReadAsync(buffer, 0, 1024);

to

int packetSize = acceptedClient.GetStream().Read(buffer, 0, 1024);

And the server will work exactly the same.

So, why using async / await in tcp listener for multiple users if it's the same like using regular synchrony methods?

Should I keep using IOCP in that case? because for me it's pretty easy and comfortable but I am afraid that it will be obsoleted or even no more available in newer .NET versions.

Aleuromancy answered 4/10, 2013 at 17:10 Comment(6)
await is not synchronous. In particular, it will not block any thread.Shaveling
First, thanks for your answer. Why should i care that it won't block any thread if i open the function that accept clients or receive data from clients in new thread (new task)? again, like in regular synchronous methods.Aleuromancy
@Elad in your use case, there isn't any difference. In fact using async creates one more unnecessary task.Genuflect
I heard that using my code with async / await requires less work from the cpu - is that right? if yes is it right also when comparing it to IOCP?Aleuromancy
@Elad: No. The performance between APM and TAP is essentially identical; the benefit of TAP is that your code is (usually) more maintainable. You do want to use asynchronous methods (APM/EAP/TAP) for sockets both for scalability reasons and because (in the general case) synchronous APIs can cause deadlocks.Rhizobium
However, I strongly recommend hosting WebAPI instead of writing your own socket. TCP/IP application protocol design is rather tricky, and I recommend using WebAPI (plus SignalR if necessary) which takes care of all the hard parts for you.Rhizobium
T
17

Untill now, i used IOCP for tcp servers which was pretty easy and comfortable, but this time i want to use the Async / Await tech. that was released in C# 5.0.

I think you need to get your terminology right.

Having BeginOperation and EndOperation methods is called Asynchronous Programming Model (APM). Having a single Task (or Task<T>) returning method is called Task-based Asynchronous Pattern (TAP). I/O Completion Ports (IOCP) are a way to handle asynchronous operations on Windows and asynchronous I/O methods using both APM and TAP use them.

What this means is that the performance of APM and TAP is going to be very similar. The big difference between the two is that code using TAP and async-await is much more readable than code using APM and callbacks.

So, if you want to (or have to) write your code asynchronously, use TAP and async-await, if you can. But if you don't have a good reason to do that, just write your code synchronously.

On the server, a good reason to use asynchrony is scalability: asynchronous code can handle many more requests at the same time, because it tends to use fewer threads. If you don't care about scalability (for example because you're not going to have many users at the same time), then asynchrony doesn't make much sense.


Also, your code contains some practices that you should avoid:

  • Don't use async void methods, there is no good way to tell when they complete and they have bad exception handling. The exception is event handlers, but that applies mostly to GUI applications.
  • Don't use Task.Run() if you don't have to. Task.Run() can be useful if you want to leave the current thread (usually the UI thread) or if you want to execute synchronous code in parallel. But it doesn't make much sense to use it to start asynchronous operations in server applications.
  • Don't ignore Tasks returned from methods, unless you're sure they're not going to throw an exception. Exceptions from ignored Tasks won't do anything, which could very easily mask a bug.
Trig answered 4/10, 2013 at 18:7 Comment(4)
So if I got you right, you're saying that in my code example the async / await is unnecessary, and I should decide which option is better for me?: 1. create a server like I did in the code example without async / await 2. create a server using APMAleuromancy
@Elad No, that's not what I'm saying. I'm saying you should use async-await if you need scalability and synchronous code if you don't. APM pretty much has no advantage against TAP.Trig
So I dont get how I should write the server right with async await, because right now it seems to me that using async / await and using regular synchronous methods is the same thing (like i show in the code example), and the async await doesnt save all the dirty work with openning new threads and creating while (true) loops.. Do you maybe have a good code sample for me?Aleuromancy
I know it's been many years, but maybe for other people stumbling on your comment @Aleuromancy and thinking the same: async/await and syncronous might seem similar but they are not the same; when you await something, the thread that was executing your code goes off and does something else while the awaited Task is completing. When you call a method syncronously, the thread does nothing other than wait for the call to complete. This is why with async you don't need threads "to carry on ensuring things are done while methods are taking time to complete" - threads keep busy "doing other stuff"Fayum
D
1

After couple of search i found this

Q: It is a list of TCPServer practices but which one is the best practice for managing 5000+ clients each seconds ?

A: my assumption is "Writing async methods" more over if you are working with database same time "Async methods and iterators" will do more

Discoverer answered 15/1, 2016 at 6:31 Comment(0)
B
0

here is the sample code using async/await.

async await tcp server

I think it is easier to build a tcp server using async/await than iocp.

Bouncer answered 22/11, 2017 at 6:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.