private bool AddClient(ClientMetadata client)
        {
            ClientMetadata removedClient;

            if (!Clients.TryRemove(client.IpPort(), out removedClient))
            {
                // do nothing, it probably did not exist anyway
            }

            Clients.TryAdd(client.IpPort(), client);
            Log("AddClient added client " + client.IpPort());
            return(true);
        }
        private async Task DataReceiver(ClientMetadata client, CancellationToken?cancelToken = null)
        {
            try
            {
                #region Wait-for-Data

                while (true)
                {
                    cancelToken?.ThrowIfCancellationRequested();

                    try
                    {
                        if (!IsConnected(client))
                        {
                            break;
                        }

                        byte[] data = await MessageReadAsync(client);

                        if (data == null)
                        {
                            // no message available
                            await Task.Delay(30);

                            continue;
                        }

                        if (MessageReceived != null)
                        {
                            var unawaited = Task.Run(() => MessageReceived(client.IpPort(), data));
                        }
                    }
                    catch (Exception)
                    {
                        break;
                    }
                }

                #endregion
            }
            finally
            {
                ActiveClients--;
                RemoveClient(client);
                if (ClientDisconnected != null)
                {
                    var unawaited = Task.Run(() => ClientDisconnected(client.IpPort()));
                }
                Log("DataReceiver client " + client.IpPort() + " disconnected (now " + ActiveClients + " clients active)");
            }
        }
        private bool RemoveClient(ClientMetadata client)
        {
            ClientMetadata removedClient;

            if (!Clients.TryRemove(client.IpPort(), out removedClient))
            {
                Log("RemoveClient unable to remove client " + client.IpPort());
                return(false);
            }
            else
            {
                Log("RemoveClient removed client " + client.IpPort());
                return(true);
            }
        }
        private async Task <bool> MessageWriteAsync(ClientMetadata client, byte[] data)
        {
            try
            {
                #region Format-Message

                string header = "";
                byte[] headerBytes;
                byte[] message;

                if (data == null || data.Length < 1)
                {
                    header += "0:";
                }
                else
                {
                    header += data.Length + ":";
                }

                headerBytes = Encoding.UTF8.GetBytes(header);
                int messageLen = headerBytes.Length;
                if (data != null && data.Length > 0)
                {
                    messageLen += data.Length;
                }

                message = new byte[messageLen];
                Buffer.BlockCopy(headerBytes, 0, message, 0, headerBytes.Length);

                if (data != null && data.Length > 0)
                {
                    Buffer.BlockCopy(data, 0, message, headerBytes.Length, data.Length);
                }

                #endregion

                #region Send-Message-Async

                var clientStream = client.Tcp.GetStream();
                await clientStream.WriteAsync(message, 0, message.Length);

                await clientStream.FlushAsync();

                return(true);

                #endregion
            }
            catch (Exception)
            {
                Log("*** MessageWriteAsync " + client.IpPort() + " disconnected due to exception");
                return(false);
            }
        }
        private async Task <byte[]> MessageReadAsync(ClientMetadata client)
        {
            /*
             *
             * Do not catch exceptions, let them get caught by the data reader
             * to destroy the connection
             *
             */

            #region Variables

            int  bytesRead      = 0;
            int  sleepInterval  = 25;
            int  maxTimeout     = 500;
            int  currentTimeout = 0;
            bool timeout        = false;

            NetworkStream ClientStream = client.Tcp.GetStream();

            byte[] headerBytes;
            string header = "";
            long   contentLength;
            byte[] contentBytes;

            if (!ClientStream.CanRead)
            {
                return(null);
            }
            if (!ClientStream.DataAvailable)
            {
                return(null);
            }

            #endregion

            #region Read-Header

            using (MemoryStream headerMs = new MemoryStream())
            {
                #region Read-Header-Bytes

                byte[] headerBuffer = new byte[1];
                timeout        = false;
                currentTimeout = 0;
                int read = 0;

                while ((read = await ClientStream.ReadAsync(headerBuffer, 0, headerBuffer.Length)) > 0)
                {
                    if (read > 0)
                    {
                        await headerMs.WriteAsync(headerBuffer, 0, read);

                        bytesRead     += read;
                        currentTimeout = 0;

                        if (bytesRead > 1)
                        {
                            if ((int)headerBuffer[0] == 58)
                            {
                                break;
                            }
                        }
                    }
                    else
                    {
                        if (currentTimeout >= maxTimeout)
                        {
                            timeout = true;
                            break;
                        }
                        else
                        {
                            currentTimeout += sleepInterval;
                            await Task.Delay(sleepInterval);
                        }
                    }
                }

                if (timeout)
                {
                    Log("*** MessageReadAsync timeout " + currentTimeout + "ms/" + maxTimeout + "ms exceeded while reading header after reading " + bytesRead + " bytes");
                    return(null);
                }

                headerBytes = headerMs.ToArray();
                if (headerBytes == null || headerBytes.Length < 1)
                {
                    return(null);
                }

                #endregion

                #region Process-Header

                header = Encoding.UTF8.GetString(headerBytes);
                header = header.Replace(":", "");

                if (!Int64.TryParse(header, out contentLength))
                {
                    Log("*** MessageReadAsync malformed message from " + client.IpPort() + " (message header not an integer)");
                    return(null);
                }

                #endregion
            }

            #endregion

            #region Read-Data

            using (MemoryStream dataMs = new MemoryStream())
            {
                long bytesRemaining = contentLength;
                timeout        = false;
                currentTimeout = 0;

                int    read = 0;
                byte[] buffer;
                long   bufferSize = 2048;
                if (bufferSize > bytesRemaining)
                {
                    bufferSize = bytesRemaining;
                }
                buffer = new byte[bufferSize];

                while ((read = await ClientStream.ReadAsync(buffer, 0, buffer.Length)) > 0)
                {
                    if (read > 0)
                    {
                        dataMs.Write(buffer, 0, read);
                        bytesRead      = bytesRead + read;
                        bytesRemaining = bytesRemaining - read;
                        currentTimeout = 0;

                        // reduce buffer size if number of bytes remaining is
                        // less than the pre-defined buffer size of 2KB
                        if (bytesRemaining < bufferSize)
                        {
                            bufferSize = bytesRemaining;
                        }
                        buffer = new byte[bufferSize];

                        // check if read fully
                        if (bytesRemaining == 0)
                        {
                            break;
                        }
                        if (bytesRead == contentLength)
                        {
                            break;
                        }
                    }

                    if (!ClientStream.DataAvailable)
                    {
                        while (true)
                        {
                            if (currentTimeout >= maxTimeout)
                            {
                                timeout = true;
                                break;
                            }
                            else
                            {
                                currentTimeout += sleepInterval;
                                await Task.Delay(sleepInterval);
                            }
                        }

                        if (timeout)
                        {
                            break;
                        }
                    }
                }

                if (timeout)
                {
                    Log("*** MessageReadAsync timeout " + currentTimeout + "ms/" + maxTimeout + "ms exceeded while reading content after reading " + bytesRead + " bytes");
                    return(null);
                }

                contentBytes = dataMs.ToArray();
            }

            #endregion

            #region Check-Content-Bytes

            if (contentBytes == null || contentBytes.Length < 1)
            {
                Log("*** MessageReadAsync " + client.IpPort() + " no content read");
                return(null);
            }

            if (contentBytes.Length != contentLength)
            {
                Log("*** MessageReadAsync " + client.IpPort() + " content length " + contentBytes.Length + " bytes does not match header value " + contentLength + ", discarding");
                return(null);
            }

            #endregion

            return(contentBytes);
        }
        private async Task AcceptConnections()
        {
            Listener.Start();
            while (true)
            {
                #region Accept-Connection

                Token.ThrowIfCancellationRequested();
                TcpClient client = await Listener.AcceptTcpClientAsync();

                client.LingerState.Enabled = false;

                #endregion

                #region Get-Tuple-and-Check-IP

                string clientIp   = ((IPEndPoint)client.Client.RemoteEndPoint).Address.ToString();
                int    clientPort = ((IPEndPoint)client.Client.RemoteEndPoint).Port;

                if (PermittedIps != null && PermittedIps.Count > 0)
                {
                    if (!PermittedIps.Contains(clientIp))
                    {
                        Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                        client.Close();
                        return;
                    }
                }

                Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort);

                #endregion

                var unawaited = Task.Run(() =>
                {
                    #region Add-to-Client-List

                    ActiveClients++;
                    // Do not decrement in this block, decrement is done by the connection reader

                    ClientMetadata currClient = new ClientMetadata(client);
                    if (!AddClient(currClient))
                    {
                        Log("*** AcceptConnections unable to add client " + currClient.IpPort());
                        client.Close();
                        return;
                    }

                    #endregion

                    #region Start-Data-Receiver

                    CancellationToken dataReceiverToken = default(CancellationToken);

                    Log("AcceptConnections starting data receiver for " + currClient.IpPort() + " (now " + ActiveClients + " clients)");
                    if (ClientConnected != null)
                    {
                        Task.Run(() => ClientConnected(currClient.IpPort()));
                    }

                    Task.Run(async() => await DataReceiver(currClient, dataReceiverToken), dataReceiverToken);

                    #endregion
                }, Token);
            }
        }
Exemple #7
0
        private async Task AcceptConnections()
        {
            Listener.Start();
            while (true)
            {
                #region Accept-Connection

                Token.ThrowIfCancellationRequested();
                TcpClient tcpClient = await Listener.AcceptTcpClientAsync();

                tcpClient.LingerState.Enabled = false;

                #endregion

                #region Get-Tuple-and-Check-IP

                string clientIp   = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Address.ToString();
                int    clientPort = ((IPEndPoint)tcpClient.Client.RemoteEndPoint).Port;

                if (PermittedIps != null && PermittedIps.Count > 0)
                {
                    if (!PermittedIps.Contains(clientIp))
                    {
                        Log("*** AcceptConnections rejecting connection from " + clientIp + " (not permitted)");
                        tcpClient.Close();
                        return;
                    }
                }

                Log("AcceptConnections accepted connection from " + clientIp + ":" + clientPort);

                #endregion

                #region Initialize-and-Authenticate

                SslStream sslStream = null;
                if (AcceptInvalidCerts)
                {
                    // accept invalid certs
                    sslStream = new SslStream(tcpClient.GetStream(), false, new RemoteCertificateValidationCallback(AcceptCertificate));
                }
                else
                {
                    // do not accept invalid SSL certificates
                    sslStream = new SslStream(tcpClient.GetStream(), false);
                }

                sslStream.AuthenticateAsServer(SslCertificate, true, SslProtocols.Tls12, false);

                if (!sslStream.IsEncrypted)
                {
                    Log("*** AcceptConnections stream from " + clientIp + " not encrypted");
                    tcpClient.Close();
                    return;
                }

                if (!sslStream.IsAuthenticated)
                {
                    Log("*** AcceptConnections stream from " + clientIp + " not authenticated");
                    tcpClient.Close();
                    return;
                }

                if (MutuallyAuthenticate && !sslStream.IsMutuallyAuthenticated)
                {
                    Log("*** AcceptConnections stream from " + clientIp + " failed mutual authentication");
                    tcpClient.Close();
                    return;
                }

                #endregion

                var unawaited = Task.Run(() =>
                {
                    #region Add-to-Client-List

                    ActiveClients++;
                    // Do not decrement in this block, decrement is done by the connection reader

                    ClientMetadata currClient = new ClientMetadata(tcpClient, sslStream);
                    if (!AddClient(currClient))
                    {
                        Log("*** AcceptConnections unable to add client " + currClient.IpPort());
                        tcpClient.Close();
                        return;
                    }

                    #endregion

                    #region Start-Data-Receiver

                    CancellationToken dataReceiverToken = default(CancellationToken);

                    Log("AcceptConnections starting data receiver for " + currClient.IpPort() + " (now " + ActiveClients + " clients)");
                    if (ClientConnected != null)
                    {
                        Task.Run(() => ClientConnected(currClient.IpPort()));
                    }

                    Task.Run(async() => await DataReceiver(currClient, dataReceiverToken), dataReceiverToken);

                    #endregion
                }, Token);
            }
        }