0

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

Daniele Salvatore Albano
  • 1,263
  • 2
  • 13
  • 29
  • Are your clients on different machines to your server? – Len Holgate May 07 '13 at 06:34
  • Yes, but i had the same problem working on different machines (1gbit network). I made some changes and i must do another test with different machines. But it's strange because sometimes it works correctly sometimes not. – Daniele Salvatore Albano May 07 '13 at 17:41
  • Could you define clearly what you mean by "losing events" are you not seeing write completions that you expect to see? or is it something else. – Len Holgate Nov 18 '13 at 09:21
  • Yes, i dont see the completition ports callback called – Daniele Salvatore Albano Nov 18 '13 at 11:41
  • And you NEVER get them, or they're just delayed and really slow? See here re TCP flow control and how async send completions can be delayed by the peer and the TCP window (http://www.serverframework.com/asynchronousevents/2011/06/tcp-flow-control-and-asynchronous-writes.html) – Len Holgate Nov 18 '13 at 12:06
  • I never get them ... i don't have any problem with the send buffer because i send one packet per time (i use a AutoResetEvent to handle async send requests with a timeout to avoid deadlocks) so if the receiver is full the send buffer will never get full and the server will wait up to 30 seconds before killing the connection (like a send timeout) – Daniele Salvatore Albano Nov 23 '13 at 09:39

0 Answers0