43

I've previously used BeginAccept() and BeginRead(), but with Visual Studio 2012 I want to make use of the new asynchronous (async, await) features in my socket server program.

How can I complete the AcceptAsync and ReceiveAsync functions?

using System.Net;
using System.Net.Sockets;

namespace OfficialServer.Core.Server
{
    public abstract class CoreServer
    {
        private const int ListenLength = 500;
        private const int ReceiveTimeOut = 30000;
        private const int SendTimeOut = 30000;
        private readonly Socket _socket;

        protected CoreServer(int port, string ip = "0.0.0.0")
        {
            _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            _socket.Bind(new IPEndPoint(IPAddress.Parse(ip), port));
            _socket.Listen(ListenLength);
            _socket.ReceiveTimeout = ReceiveTimeOut;
            _socket.SendTimeout = SendTimeOut;
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            _socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.DontLinger, true);
        }

        public void Start()
        {    
        }
    }
}
1
  • Aren't SendTimeout and ReceiveTimeout only applicable in the synchronous case? Commented Sep 6, 2019 at 6:40

2 Answers 2

62

...because you're so determined, I put together a very simple example of how to write an echo server to get you on your way. Anything received gets echoed back to the client. The server will stay running for 60s. Try telnetting to it on localhost port 6666. Take time to understand exactly what's going on here.

void Main()
{
    CancellationTokenSource cts = new CancellationTokenSource();
    TcpListener listener = new TcpListener(IPAddress.Any, 6666);
    try
    {
        listener.Start();
        //just fire and forget. We break from the "forgotten" async loops
        //in AcceptClientsAsync using a CancellationToken from `cts`
        AcceptClientsAsync(listener, cts.Token);
        Thread.Sleep(60000); //block here to hold open the server
    }
    finally
    {
        cts.Cancel();
        listener.Stop();
    }
}

async Task AcceptClientsAsync(TcpListener listener, CancellationToken ct)
{
    var clientCounter = 0;
    while (!ct.IsCancellationRequested)
    {
        TcpClient client = await listener.AcceptTcpClientAsync()
                                            .ConfigureAwait(false);
        clientCounter++;
        //once again, just fire and forget, and use the CancellationToken
        //to signal to the "forgotten" async invocation.
        EchoAsync(client, clientCounter, ct);
    }

}
async Task EchoAsync(TcpClient client,
                     int clientIndex,
                     CancellationToken ct)
{
    Console.WriteLine("New client ({0}) connected", clientIndex);
    using (client)
    {
        var buf = new byte[4096];
        var stream = client.GetStream();
        while (!ct.IsCancellationRequested)
        {
            //under some circumstances, it's not possible to detect
            //a client disconnecting if there's no data being sent
            //so it's a good idea to give them a timeout to ensure that 
            //we clean them up.
            var timeoutTask = Task.Delay(TimeSpan.FromSeconds(15));
            var amountReadTask = stream.ReadAsync(buf, 0, buf.Length, ct);
            var completedTask = await Task.WhenAny(timeoutTask, amountReadTask)
                                          .ConfigureAwait(false);
            if (completedTask == timeoutTask)
            {
                var msg = Encoding.ASCII.GetBytes("Client timed out");
                await stream.WriteAsync(msg, 0, msg.Length);
                break;
            }
            //now we know that the amountTask is complete so
            //we can ask for its Result without blocking
            var amountRead = amountReadTask.Result;
            if (amountRead == 0) break; //end of stream.
            await stream.WriteAsync(buf, 0, amountRead, ct)
                        .ConfigureAwait(false);
        }
    }
    Console.WriteLine("Client ({0}) disconnected", clientIndex);
}
Sign up to request clarification or add additional context in comments.

18 Comments

Thank you very much this helped alot understanding how things going.
No problem. You seemed a little confused about the best approach, so hopefully this will clear things up a little.
But one last question for now :D, is there a difference between using the Old BeginReceive, and the new ReceiveAsync in the performance ? or it is kinda the same ?!
Socket.ReceiveAsync is a strange one. It has nothing to do with async/await features in .net4.5. It was designed as an alternative socket API that wouldn't thrash memory as hard as BeginReceive/EndReceive, and only needs to be used in the most hardcore of server apps. To give you some sense of scale, we run a server BeginXXXX/EndXXXX methods that support 5000 connected clients with ease. I've never had any need to use ReceiveAsync, and I'd probably upgrade hardware before considering a rewrite using ReceiveAsync and SocketAsyncEventArgs because it would probably be cheaper in terms of my time.
...or migrate to async/await methods that have superceded them, as with my example.
|
16

You can use TaskFactory.FromAsync to wrap up Begin / End pairs into async-ready operations.

Stephen Toub has an awaitable Socket on his blog which wraps the more efficient *Async endpoints. I recommend combining this with TPL Dataflow to create a fully async-compatible Socket component.

8 Comments

I don't wanna to wrap Begin and End (if i understand you correctly). what i want to do is to Use .AcceptAsync instead of .BeginAccept, And .ReceiveAsync instead of .BeginReceive
AcceptAsync and ReceiveAsync use a special form of asynchronous API that only exists for the Socket class. They have nothing to do with async and await.
:D yea thats what i wanted but i can't achieve using SocketAsyncEventArgs, i don't know how. if you could provide me with an example of accepting connections, receiving data from them using those methods i will appreciate it alot
Follow the link in my last comment. There's a long example on that MSDN page. The usual caveats with MSDN socket examples apply: it probably does not handle edge conditions correctly and will almost definitely need to change for any kind of realistic protocol.
I never told that i am struggling, Also i used to create Asynchronous Sockets and it is not hard at all for me but i just wanted to Use SocketAsyncEventArgs and it is kinda new to me this is why i am just asking.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.