SocketAsyncEventArgs CreateArgs()
        {
            var args = new SocketAsyncEventArgs();

            args.Completed += IO_Completed;

            var token = new AsyncUserToken
            {
                Buffer = new byte[SocketConfig.TcpReaderBufferSize]
            };

            args.UserToken = token;
            args.SetBuffer(token.Buffer, 0, SocketConfig.TcpReaderBufferSize);
            return(args);
        }
        // This method is invoked when an asynchronous receive operation (by the socket) completes.
        // If the remote host already closed the connection, then the socket is already closed and we just decrement the counter.
        // If SocketError.Success, we process the client command and reply immediately (we always send an 'ack').
        void ProcessReceive(SocketAsyncEventArgs e)
        {
            AsyncUserToken token = (AsyncUserToken)e.UserToken;

            if (e.BytesTransferred > 0 && e.SocketError == SocketError.Success)
            {
                try
                {
                    NOTLSEnvelopeExtensions.UpdatePayload(e.BytesTransferred, token);

                    do
                    {
                        NOTLSEnvelope packet = NOTLSEnvelopeExtensions.TryTakeOnePacket(ref token.Payload);
                        if (packet == null)                         // null -> not yet complete
                        {
                            if (!token.Socket.ReceiveAsync(e))
                            {
                                ProcessReceive(e);
                            }
                            return;
                        }

                        var    clientInformation = token.Socket.RemoteEndPoint.ToString();
                        byte[] reply             = this.requestHandler.ProcessRequestAsync(packet.Serialize(), clientInformation).Result;


                        if (reply != null)
                        {
                            SocketAsyncEventArgs sendArgs = CreateArgs();
                            ((AsyncUserToken)sendArgs.UserToken).Socket = token.Socket;
                            sendArgs.SetBuffer(reply, 0, reply.Length);
                            if (!token.Socket.SendAsync(sendArgs))
                            {
                                ProcessSend(sendArgs);
                            }
                        }
                    } while (token.Payload != null);
                }
                catch (Exception ex)
                {
                    this.logger.LogError($"ProcessReceive - {ex}");
                }
            }
            else
            {
                CloseClientSocket(e);
            }
        }
        void CloseClientSocket(SocketAsyncEventArgs e)
        {
            AsyncUserToken token = e?.UserToken as AsyncUserToken;

            try
            {
                if (token == null || token.Socket == null)
                {
#if DEBUG
                    this.logger.LogInformation("CloseClientSocket - token or token.Socket was null. Doing nothing.");
#endif
                    return;
                }
                try
                {
                    token.Socket.Shutdown(SocketShutdown.Send);
                }
                catch (Exception ex)                 // throws if client process has already closed
                {
                    this.logger.LogError($"CloseClientSocket - Socket.Shutdown(SocketShutdown.Send) - {ex.Message}");
                }
                try
                {
                    token.Socket.Dispose();
                }
                catch (Exception ex)
                {
                    this.logger.LogError($"CloseClientSocket - Socket.Dispose() - {ex.Message}");
                }
            }
            finally
            {
                // decrement connection counter
                Interlocked.Decrement(ref NumConnectedSockets);
                this.maxNumberAcceptedClients.Release();
            }
#if DEBUG
            this.logger.LogInformation($"Socket closed, {NumConnectedSockets} connected sockets.");
#endif
            // Once we use the buffer manager:
            // Free the SocketAsyncEventArg for reuse by another client
            // readWritePool.Push(e);
        }
        void ProcessSend(SocketAsyncEventArgs e)
        {
            if (e.SocketError == SocketError.Success)
            {
                // done writing to the client
                AsyncUserToken token = (AsyncUserToken)e.UserToken;

                SocketAsyncEventArgs readArgs = CreateArgs();
                ((AsyncUserToken)readArgs.UserToken).Socket = token.Socket;                 // copy the _right_ socket

                // read the next message from the client
                bool willRaiseEvent = token.Socket.ReceiveAsync(readArgs);
                if (!willRaiseEvent)
                {
                    ProcessReceive(readArgs);
                }
            }
            else
            {
                CloseClientSocket(e);
            }
        }