High performance TCP server in C#
Solution 1:
It must be async, there is no way around this. High performance and scalability don't mix with one-thread-per-socket. You can have a look at what StackExchange themselves are doing, see async Redis await BookSleeve which leverages the CTP features from the next C# release (so is on the edge and subject to changes, but it is cool). For even more bleeding edge the solutions evolves around leveraging SocketAsyncEventArgs Class which takes things one step further by eliminating the frequent allocations of async handlers associated with 'classic' C# async processing:
The SocketAsyncEventArgs class is part of a set of enhancements to the System.Net.Sockets.Socket class that provide an alternative asynchronous pattern that can be used by specialized high-performance socket applications. This class was specifically designed for network server applications that require high performance. An application can use the enhanced asynchronous pattern exclusively or only in targeted hot areas (for example, when receiving large amounts of data).
Long story short: learn async or die trying...
BTW, if you're asking why async, then read the three articles linked from this post: High Performance Windows programs. The ultimate answer is: the underlying OS design requires it.
Solution 2:
As Remus says above, you have to use async to keep performance high. That is the Begin.../End... methods in .NET.
Under the hood for sockets, these methods make use of IO Completion Ports which seems to be the most performant way of processing many sockets on Windows operating systems.
As Jim says, the TcpClient class can help here and is pretty easy to use. Here is an example of using the TcpListener to listen for incoming connections and the TcpClient to handle them, with the initial BeginAccept and BeginRead calls being async.
This example does assume a message based protocol is used over the sockets and that is ommitted except that the first 4 bytes of each transmission is the length, but that then allows you to use a synchronous Read on the stream to get the rest of the data that is already buffered.
Here is the code:
class ClientContext
{
public TcpClient Client;
public Stream Stream;
public byte[] Buffer = new byte[4];
public MemoryStream Message = new MemoryStream();
}
class Program
{
static void OnMessageReceived(ClientContext context)
{
// process the message here
}
static void OnClientRead(IAsyncResult ar)
{
ClientContext context = ar.AsyncState as ClientContext;
if (context == null)
return;
try
{
int read = context.Stream.EndRead(ar);
context.Message.Write(context.Buffer, 0, read);
int length = BitConverter.ToInt32(context.Buffer, 0);
byte[] buffer = new byte[1024];
while (length > 0)
{
read = context.Stream.Read(buffer, 0, Math.Min(buffer.Length, length));
context.Message.Write(buffer, 0, read);
length -= read;
}
OnMessageReceived(context);
}
catch (System.Exception)
{
context.Client.Close();
context.Stream.Dispose();
context.Message.Dispose();
context = null;
}
finally
{
if (context != null)
context.Stream.BeginRead(context.Buffer, 0, context.Buffer.Length, OnClientRead, context);
}
}
static void OnClientAccepted(IAsyncResult ar)
{
TcpListener listener = ar.AsyncState as TcpListener;
if (listener == null)
return;
try
{
ClientContext context = new ClientContext();
context.Client = listener.EndAcceptTcpClient(ar);
context.Stream = context.Client.GetStream();
context.Stream.BeginRead(context.Buffer, 0, context.Buffer.Length, OnClientRead, context);
}
finally
{
listener.BeginAcceptTcpClient(OnClientAccepted, listener);
}
}
static void Main(string[] args)
{
TcpListener listener = new TcpListener(new IPEndPoint(IPAddress.Any, 20000));
listener.Start();
listener.BeginAcceptTcpClient(OnClientAccepted, listener);
Console.Write("Press enter to exit...");
Console.ReadLine();
listener.Stop();
}
}
It demonstrates how to handle the async calls, but it will need error handling adding to make sure the TcpListener is always accepting new connections and more error handling for when clients disconnect unexpectedly. Also, there do seem to be a few cases where not all of the data arrives in one go that would need handling too.
Solution 3:
You can do this with the TcpClient class, although to tell the truth I don't know if you could have 10 thousand open sockets. That's quite a lot. But I regularly use TcpClient
to handle dozens of concurrent sockets. And the asynchronous model is actually very nice to use.
Your biggest problem isn't going to be making TcpClient
work. With 10 thousand concurrent connections, I'm thinking bandwidth and scalability are going to be problems. I don't even know if one machine can handle all that traffic. I suppose it depends on how large the packets are and how often they're coming in. But you'd better do some back-of-the-envelope estimation before you commit to implementing this all on a single computer.
Solution 4:
I think you are also looking for UDP techniques. For 10k clients, it is fast but the issue is you have to implement ACKnowledgement for each message that you received the message. In UDP you dont need to open a socket for each client but need to implement heartbeat/ping mechanism after x seconds to check which client is connected or not.