9

I look for the best practices to meet the following requirements:

  • A framework that handles several client sockets in an asynchronous fashion
  • Each incoming message protocol dictates that each message is of format string and is marked complete with a line feed character '\n'.
  • I have full control of the client side but not server side. Server accepts and sends string based messages with line feed character to mark completion of the message.
  • I want to be able to send messages through each connected socket at any given time (each socket can receive and send messages).
  • Incoming messages should be forwarded through a callback.
  • I want to be able to chose in my implementation whether all incoming complete messages from all connected sockets are routed to one single callback or whether each socket client implements its own callback.
  • I connect at a maximum 4 clients/sockets. So I look for suggestions that capitalize on such limited amounts of sockets, however, are capable of managing all those concurrently.

I wonder whether the framework where I use BeginReceive and EndReceive with implemented IAsyncResult callback is state of the art given I target .Net 4.5. Is there a better solution, such as using NetworkStream or other API choices? What really bugs me with the BeginReceive/EndReceive implementation is that after EndReceive I have to call BeginReceive again and register the callback again. That sounds like an awful amount of overhead to me. Why can't new data be added async at any time and at the same time another context builds complete messages that are then routed through a raised event?

The argument of using IAsyncResult is often given in that thread handling is taken care of, but what speaks against the following: Using a NetworkStream and simply read from and write to the stream. As mentioned only string messages are exchanged and each message per protocol is marked complete by the line feed character. A separate task/thread would poll a streamreader (based on the networkstream) through ReadLine(). It probably can`t get simpler than that, can it?

What I am basically asking is, how can the following code be made truly async?

public class SocketClient
{
    private TcpClient client;
    private StreamReader reader;
    private StreamWriter writer;

    public event Action<string> MessageCallback;

    public SocketClient(string hostname, int port)
    {
        client = new TcpClient(hostname, port);

        try
        {
            Stream stream = client.GetStream();
            reader = new StreamReader(stream);
            writer = new StreamWriter(stream);
            writer.AutoFlush = true;

            //Start Listener on port
            StartListener();
        }
        catch (Exception e)
        {
            throw new Exception(e.ToString());
        }
    }

    public void StartListener()
    {
        Task task = Task.Factory.StartNew(() =>
            {
                while (true)
                {
                    if (MessageCallback != null)
                    {
                        MessageCallback(reader.ReadLine());

                    }

                    //Thread.Sleep(200);
                }
            });
    }

}
Matt
  • 7,004
  • 11
  • 71
  • 117
  • if the client and server side code is completely in your control and is .net you could take a look at SignalR for handling your messaging. The downside is for WebSockets you will need to use Windows 8 or 2012 as your server. – Aron Feb 24 '13 at 07:50
  • Another possibility is for you to use Rx.Net along with Task.Factory.FromAsync to produce an IObservable, which you then subscribe to – Aron Feb 24 '13 at 07:52
  • @Freddy - Can you please clarify if you are looking for a .net library which has the requested features or how you can achieve the necessary features yourself using ONLY the .net 4.5 framework? – MarcF Feb 24 '13 at 10:53
  • @MarcF, a library suffices though I wonder what is wrong (or how is the following inferior to using BeginReceive/EndReceive): Using a NetworkStream and simply read from and write to the stream. As mentioned only string messages are exchanged and each message per protocol is marked complete by the line feed character. – Matt Feb 24 '13 at 13:06
  • @Aron, sorry if I did not come across correctly, I am not looking for an ASP/Web solution. I need to handle as much as 100 incoming string messages through TCP within a C# console application. I have no control over the server and the protocol is fixed. – Matt Feb 24 '13 at 13:32

2 Answers2

14

There is no current standard or common practice at the moment. You have a number of choices, each with advantages and disadvantages:

  1. Wrap the TAP methods into Tasks and use async/await.
    • Advantages: Pretty straightforward to do.
    • Disadvantages: It's low-level; you have to manage all the various async operations yourself within an "infinite" loop as well as handle state management for each connection.
  2. Wrap the Socket *Async methods into Tasks and use async/await.
    • Advantages: Speed. This is the fastest and most scalable option.
    • Disadvantages: You still have low-level "infinite" loops and state management, where the code is more complex than option (1).
  3. Convert socket completions to Rx events.
    • Advantages: You can encapsulate the "infinite" loops and treat the completions as a stream of events.
    • Disadvantages: There's a hefty learning curve to Rx, and this is not a simple use case. Managing the state of each connection can get complex.
  4. Convert socket completions to TPL Dataflow.
    • Advantages: (same as Rx): encapsulating the loops and getting a stream of data.
    • Disadvantages: The learning curve is easier than Rx, but you still have some complex state management for each connection.
  5. Use an existing library such as my Nito.Async library, which provides EAP socket classes.
    • Advantages: Very easy to use; everything is an event and there's no multithreading concerns. Also, the tricker parts of state management are done for you.
    • Disadvantages: Doesn't scale as well as the lower-level solutions.

For your situation (a few hundred messages per second on less than a hundred sockets), I would recommend using my Nito.Async library. It's the easiest one of these options to get working.

Regarding your protocol, you'll have to parse out the \ns by hand and do your own buffering. (That's true for all the choices above).

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • Great overview and summary. Exactly what I was looking for. Thanks a lot. – Matt Feb 25 '13 at 18:47
  • one more question, where does my code fits into your different approaches? I tried to work with async/await but ran into run-time errors when sending messages async I guess because a previous send was still processed when a new send hit the socket. Where would a solution fit in that runs within a loop within its own task but send/receive synchronously? – Matt Feb 26 '13 at 06:21
  • I implemented the second option in the end, Stephen Toub's awaiting socket implementation. Thanks for pointing me that way. It works a treat. – Matt Feb 26 '13 at 09:48
  • @Freddy I had said already this in my answer `I think you can jump to last paragraph's link.` For the [awaiting socket example](http://blogs.msdn.com/b/pfxteam/archive/2011/12/15/10248293.aspx) from Stephen Toub from Parallel programming team blog :P – Harsh Baid Feb 27 '13 at 06:37
  • @HarshBaid, I think it is pretty clear that Stephen's answer is a lot more comprehensive and and I already pointed out that his was the answer I was seeking. thanks – Matt Feb 27 '13 at 06:50
  • @stephen-cleary I know it's an older question, but I'm wondering if you can wrap the *Async methods with the TaskCompletionSource instead of creating an awaitable object? – Josh Dec 05 '19 at 12:19
  • @Josh: I'm not really following; how is that different than option (2) in my answer? – Stephen Cleary Dec 05 '19 at 18:02
  • @StephenCleary never mind. I mixed something up. But if you have time you could have a look on a more specific question I have: https://stackoverflow.com/questions/59209996/c-sharp-how-to-cancel-custom-awaitable. Your advise is highly appreciated. I love your book btw. Useful examples and good explained in few words. – Josh Dec 06 '19 at 09:14
1

As per my recommendation please use new Async form of XXXReceive and 'XXXSend' (where XXX stands for Begin and End), the new methods available are ReceiveAsync and SendAsync methods which uses SocketAsyncEventArgs to pass the socket and other information on the callbacks event handlers.

I had seen a good working example of socket client and server on msdn archive which was scalable upto 500 connections (As I had tested in one of project adaptation from it) but currently I am not able to find that link from googling.. but here is another link from msdn archive on the same topic hopefully it will help you - Get Closer to the Wire with High-Performance Sockets in .NET..

UPDATE

First of all I can only provide you idea to final implementation and if possible some short example code snippets. ok here I go with more details

I think you can jump to last paragraph's link. ;)

Let me highlight once because I want to, I said SendAsync and ReceiveAsync not BeginReceive/EndReceive and BeginSend/EndSend i.e. Event-based Async Pattern (EAP)

Benefit of using Async form of Socket methods is that they are exception-less approach to socket programming which can prove faster than BeginSend/EndSend methods.

Here is the link for the sample which I have found to be useful upto 500 PARALLEL connections - Networking Samples for .NET v4.0

For you need to use await/async feature of .NET 4.5. Here is the .NET 4.5 code snippet showing usage of WebSocket class which of can be adapted to Socket implementations also - Support for WebSockets Protocol (I guess WebSocket's AspNetWebSocketContext will be Socket's SocketAsyncEventArgs)

I found Awaiting Socket Operations sample code from MSDN - Parallel Programming team blog which can be useful to use to implement await/async from .NET 4.5 framework.

I hope this proves helpful to you.

Harsh Baid
  • 7,199
  • 5
  • 48
  • 92
  • thanks for your answer, but your suggestion is not new at all, it is exactly the one I defined above where I start off from. I am looking for alternatives, such as FromAsync which basically works off task continuations, TPL dataflow, running synchronous reads/writes of string based messages (given the simple protocol I described above) but within its own task with while loop, to just name a few. I have seen the async socket examples for server and client before, no need to post. As mentioned I look for **alternatives**. – Matt Feb 25 '13 at 06:46
  • to be honest, not much, I appreciate though. As pointed out I am looking for a non ASP type solution, I have to manage clients and have no control over the server nor am I dealing with web applications. Also, I do not see how you presented much aside the dreaded Begin/End Receive and the AsyncCallback option. – Matt Feb 25 '13 at 09:53