For work, I need to write a server able to handle up to 8000/10000 active connections using TCP as protocol.
I've used IOCP before, but I'm getting a weird situation where the server starts to lose events after high load!
To test the architecture I wrote a simply send-back-everything-you-receive client and server and it works fine up to 1000/2000 clients, but when i start to get more connections the IOCP callback doesn't get called!
Server and client are running on my local machine
If I put a Thread.Sleep in the middle of the send/receive events of 1ms all works fine , but the network throughput that the server can handle goes down a lot. I tried with Thread.Yield and Thread.Sleep(0), that is very similar to Yieald, without useful result.
My development machine is an i7-3770 with 8gb of ram, so it's able to handle this load! With the Thread.Sleep the CPU consumption is below 30% and i can handle up to 10000 connections without losing events but with a throughput of 200mbps (client+server) ... when the system doesn't lose events, without the Thread.Sleep, the server is able to handle up to 8gbps throughput!
I'm using 8192 buffer size.
To optimize at the best, my code pre-initialize ALL necessary read buffers and read/write SocketAsyncEventArgs (in the code below there is an initialization, but i've a super class that extends the one below and pre-initialize all the necessary.
Here there is some code of the server part
public bool SendAsync(byte[] Buffer, int Offset, int Length)
{
// Verifica se è connesso
if (this.IsConnected == false)
{
return false;
}
// Attende un eventuale invio in corso
this.sendAsyncAutoResetEvent.WaitOne();
if (this.sendSocketAsyncEventArgs == null)
{
// Inizializza l'oggetto contenente le informazioni per la gestione asincrona (IOCP)
// dell'invio dei dati
this.sendSocketAsyncEventArgs = new SocketAsyncEventArgs();
this.sendSocketAsyncEventArgs.Completed += new EventHandler<SocketAsyncEventArgs>(
this.SocketAsyncCallback);
}
try
{
this.sendSocketAsyncEventArgs.SetBuffer(Buffer, Offset, Length);
}
catch (ObjectDisposedException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
catch (NullReferenceException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
return this.SendAsyncInternal();
}
private bool SendAsyncInternal()
{
try
{
// Prova ad avviare la ricezione asincrona
if (this.socket.SendAsync(this.sendSocketAsyncEventArgs) == false)
{
// L'operazione si è completata in modo sincrono, gestisce gli eventi
this.SocketAsyncCallback(this, this.sendSocketAsyncEventArgs);
}
}
catch (SocketException e)
{
// Imposta l'eccezione
this.lastSocketException = e;
this.sendAsyncAutoResetEvent.Set();
return false;
}
catch (ObjectDisposedException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
catch (NullReferenceException)
{
this.sendAsyncAutoResetEvent.Set();
return false;
}
return true;
}
protected void SocketAsyncCallback(object sender, SocketAsyncEventArgs e)
{
ThreadPool.QueueUserWorkItem(new WaitCallback(this.ProcessSocketEvent), e);
}
private void ProcessSocketEvent(object State)
{
SocketAsyncEventArgs e = State as SocketAsyncEventArgs;
try
{
if
(
// Richiesta chiusura della connessione
(
e.LastOperation != SocketAsyncOperation.Connect
&&
e.SocketError == SocketError.Success
&&
e.BytesTransferred == 0
)
||
e.SocketError == SocketError.OperationAborted
||
e.SocketError == SocketError.ConnectionReset
)
{
// Termina la connessione
this.Close();
}
else if (e.SocketError == SocketError.ConnectionRefused)
{
this.ProcessSocketConnectRefused(e);
}
else
{
// Verifica la presenza di eventuali errori
if (e.SocketError != SocketError.Success)
{
throw new Exception(e.SocketError.ToString());
}
switch (e.LastOperation)
{
case SocketAsyncOperation.Connect:
this.ProcessSocketConnect(e);
break;
case SocketAsyncOperation.Receive:
this.ProcessSocketReceive(e);
break;
case SocketAsyncOperation.Send:
this.ProcessSocketSend(e);
this.sendAsyncAutoResetEvent.Set();
break;
default:
break;
}
}
}
catch (ObjectDisposedException)
{
// do nothing
}
catch (NullReferenceException)
{
// do nothing
}
}
private void ProcessSocketConnect(SocketAsyncEventArgs e)
{
this.OnConnectionEstablished();
}
private void ProcessSocketConnectRefused(SocketAsyncEventArgs e)
{
this.OnConnectionRefused();
}
private void ProcessSocketReceive(SocketAsyncEventArgs e)
{
this.lastPacketRecievedAt = DateTime.Now;
this.OnDataReceived(e.Buffer, e.Offset, e.BytesTransferred);
}
private void ProcessSocketSend(SocketAsyncEventArgs e)
{
this.lastPacketSendedAt = DateTime.Now;
this.OnDataSended(e.Buffer, e.Offset, e.BytesTransferred);
}
Thanks for your help!
EDIT
I forgot to specify that the Operating System is Windows 8 64Bit