/// <summary>
        /// Disconnects the specified client.
        /// </summary>
        /// <param name="ipPort">IP:port of the client.</param>
        public void DisconnectClient(string ipPort)
        {
            if (String.IsNullOrEmpty(ipPort))
            {
                throw new ArgumentNullException(nameof(ipPort));
            }

            ClientMetadata client = null;

            if (!_Clients.TryGetValue(ipPort, out client))
            {
                Logger?.Invoke(_Header + "unable to find client: " + ipPort);
            }
            else
            {
                if (!_ClientsTimedout.ContainsKey(ipPort))
                {
                    Logger?.Invoke(_Header + "kicking: " + ipPort);
                    _ClientsKicked.TryAdd(ipPort, DateTime.Now);
                }
            }

            if (client != null)
            {
                if (!client.TokenSource.IsCancellationRequested)
                {
                    client.TokenSource.Cancel();
                    Logger?.Invoke(_Header + "requesting disposal of: " + ipPort);
                }

                client.Dispose();
            }
        }
Exemple #2
0
        private async Task SendInternalAsync(string ipPort, long contentLength, Stream stream)
        {
            ClientMetadata client = null;

            if (!_Clients.TryGetValue(ipPort, out client))
            {
                return;
            }
            if (client == null)
            {
                return;
            }

            long bytesRemaining = contentLength;
            int  bytesRead      = 0;

            byte[] buffer = new byte[_StreamBufferSize];

            try
            {
                await client.SendLock.WaitAsync();

                while (bytesRemaining > 0)
                {
                    bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length);

                    if (bytesRead > 0)
                    {
                        if (!_Ssl)
                        {
                            await client.NetworkStream.WriteAsync(buffer, 0, bytesRead);
                        }
                        else
                        {
                            await client.SslStream.WriteAsync(buffer, 0, bytesRead);
                        }

                        bytesRemaining   -= bytesRead;
                        _Stats.SentBytes += bytesRead;
                    }
                }

                if (!_Ssl)
                {
                    await client.NetworkStream.FlushAsync();
                }
                else
                {
                    await client.SslStream.FlushAsync();
                }
            }
            finally
            {
                if (client != null)
                {
                    client.SendLock.Release();
                }
            }
        }
Exemple #3
0
        /// <summary>
        /// Determines if a client is connected by its IP:port.
        /// </summary>
        /// <param name="ipPort">The client IP:port string.</param>
        /// <returns>True if connected.</returns>
        public bool IsConnected(string ipPort)
        {
            if (String.IsNullOrEmpty(ipPort))
            {
                throw new ArgumentNullException(nameof(ipPort));
            }

            ClientMetadata client = null;

            return(_Clients.TryGetValue(ipPort, out client));
        }
        private async Task <byte[]> DataReadAsync(ClientMetadata client, CancellationToken token)
        {
            byte[] buffer = new byte[_Settings.StreamBufferSize];
            int    read   = 0;

            if (!_Ssl)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    while (true)
                    {
                        read = await client.NetworkStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                        if (read > 0)
                        {
                            await ms.WriteAsync(buffer, 0, read, token).ConfigureAwait(false);

                            return(ms.ToArray());
                        }
                        else
                        {
                            throw new SocketException();
                        }
                    }
                }
            }
            else
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    while (true)
                    {
                        read = await client.SslStream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                        if (read > 0)
                        {
                            await ms.WriteAsync(buffer, 0, read, token).ConfigureAwait(false);

                            return(ms.ToArray());
                        }
                        else
                        {
                            throw new SocketException();
                        }
                    }
                }
            }
        }
Exemple #5
0
        private async Task <bool> StartTls(ClientMetadata client)
        {
            try
            {
                await client.SslStream.AuthenticateAsServerAsync(
                    _SslCertificate,
                    MutuallyAuthenticate,
                    SslProtocols.Tls12,
                    !AcceptInvalidCertificates);

                if (!client.SslStream.IsEncrypted)
                {
                    Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " not encrypted, disconnecting");
                    client.Dispose();
                    return(false);
                }

                if (!client.SslStream.IsAuthenticated)
                {
                    Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " not SSL/TLS authenticated, disconnecting");
                    client.Dispose();
                    return(false);
                }

                if (MutuallyAuthenticate && !client.SslStream.IsMutuallyAuthenticated)
                {
                    Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " failed mutual authentication, disconnecting");
                    client.Dispose();
                    return(false);
                }
            }
            catch (Exception e)
            {
                Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " SSL/TLS exception: " + Environment.NewLine + e.ToString());
                client.Dispose();
                return(false);
            }

            return(true);
        }
        private async Task <bool> StartTls(ClientMetadata client)
        {
            try
            {
                await client.SslStream.AuthenticateAsServerAsync(
                    _SslCertificate,
                    MutuallyAuthenticate,
                    SslProtocols.Tls12,
                    !AcceptInvalidCertificates);

                if (!client.SslStream.IsEncrypted)
                {
                    Log("[" + client.IpPort + "] not encrypted");
                    client.Dispose();
                    return(false);
                }

                if (!client.SslStream.IsAuthenticated)
                {
                    Log("[" + client.IpPort + "] stream not authenticated");
                    client.Dispose();
                    return(false);
                }

                if (MutuallyAuthenticate && !client.SslStream.IsMutuallyAuthenticated)
                {
                    Log("[" + client.IpPort + "] failed mutual authentication");
                    client.Dispose();
                    return(false);
                }
            }
            catch (Exception e)
            {
                Log("[" + client.IpPort + "] TLS exception" + Environment.NewLine + e.ToString());
                client.Dispose();
                return(false);
            }

            return(true);
        }
Exemple #7
0
        /// <summary>
        /// Send data to the specified client by IP:port.
        /// </summary>
        /// <param name="ipPort">The client IP:port string.</param>
        /// <param name="data">Byte array containing data to send.</param>
        public void Send(string ipPort, byte[] data)
        {
            if (String.IsNullOrEmpty(ipPort))
            {
                throw new ArgumentNullException(nameof(ipPort));
            }
            if (data == null || data.Length < 1)
            {
                throw new ArgumentNullException(nameof(data));
            }

            ClientMetadata client = null;

            if (!_Clients.TryGetValue(ipPort, out client))
            {
                return;
            }
            if (client == null)
            {
                return;
            }

            lock (client.SendLock)
            {
                if (!_Ssl)
                {
                    client.NetworkStream.Write(data, 0, data.Length);
                    client.NetworkStream.Flush();
                }
                else
                {
                    client.SslStream.Write(data, 0, data.Length);
                    client.SslStream.Flush();
                }
            }

            _Stats.SentBytes += data.Length;
        }
Exemple #8
0
        private async Task <byte[]> DataReadAsync(ClientMetadata client)
        {
            if (client.Token.IsCancellationRequested)
            {
                throw new OperationCanceledException();
            }
            if (!client.NetworkStream.CanRead)
            {
                return(null);
            }
            if (!client.NetworkStream.DataAvailable)
            {
                return(null);
            }
            if (_Ssl && !client.SslStream.CanRead)
            {
                return(null);
            }

            byte[] buffer = new byte[_StreamBufferSize];
            int    read   = 0;

            if (!_Ssl)
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    while (true)
                    {
                        read = await client.NetworkStream.ReadAsync(buffer, 0, buffer.Length);

                        if (read > 0)
                        {
                            ms.Write(buffer, 0, read);
                            return(ms.ToArray());
                        }
                        else
                        {
                            throw new SocketException();
                        }
                    }
                }
            }
            else
            {
                using (MemoryStream ms = new MemoryStream())
                {
                    while (true)
                    {
                        read = await client.SslStream.ReadAsync(buffer, 0, buffer.Length);

                        if (read > 0)
                        {
                            ms.Write(buffer, 0, read);
                            return(ms.ToArray());
                        }
                        else
                        {
                            throw new SocketException();
                        }
                    }
                }
            }
        }
Exemple #9
0
        private async Task DataReceiver(ClientMetadata client)
        {
            Logger?.Invoke("[SimpleTcp.Server] Data receiver started for client " + client.IpPort);

            while (true)
            {
                try
                {
                    if (client.Token.IsCancellationRequested ||
                        !IsClientConnected(client.Client))
                    {
                        Logger?.Invoke("[SimpleTcp.Server] Client " + client.IpPort + " disconnected");
                        break;
                    }

                    if (client.Token.IsCancellationRequested)
                    {
                        Logger?.Invoke("[SimpleTcp.Server] Cancellation requested (data receiver for client " + client.IpPort + ")");
                        break;
                    }

                    byte[] data = await DataReadAsync(client);

                    if (data == null)
                    {
                        await Task.Delay(30);

                        continue;
                    }

                    DataReceived?.Invoke(this, new DataReceivedFromClientEventArgs(client.IpPort, data));
                    _Stats.ReceivedBytes += data.Length;
                    UpdateClientLastSeen(client.IpPort);
                }
                catch (SocketException)
                {
                    Logger?.Invoke("[SimpleTcp.Server] Data receiver socket exception (disconnection) for " + client.IpPort);
                }
                catch (Exception e)
                {
                    Logger?.Invoke("[SimpleTcp.Server] Data receiver exception for client " + client.IpPort + ":" +
                                   Environment.NewLine +
                                   e.ToString() +
                                   Environment.NewLine);

                    break;
                }
            }

            Logger?.Invoke("[SimpleTcp.Server] Data receiver terminated for client " + client.IpPort);

            if (_ClientsKicked.ContainsKey(client.IpPort))
            {
                ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Kicked));
            }
            else if (_ClientsTimedout.ContainsKey(client.IpPort))
            {
                ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Timeout));
            }
            else
            {
                ClientDisconnected?.Invoke(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Normal));
            }

            DateTime removedTs;

            _Clients.TryRemove(client.IpPort, out ClientMetadata destroyed);
            _ClientsLastSeen.TryRemove(client.IpPort, out removedTs);
            _ClientsKicked.TryRemove(client.IpPort, out removedTs);
            _ClientsTimedout.TryRemove(client.IpPort, out removedTs);
            client.Dispose();
        }
Exemple #10
0
        private async void AcceptConnections()
        {
            while (!_Token.IsCancellationRequested)
            {
                ClientMetadata client = null;

                try
                {
                    System.Net.Sockets.TcpClient tcpClient = await _Listener.AcceptTcpClientAsync();

                    string clientIp = tcpClient.Client.RemoteEndPoint.ToString();

                    client = new ClientMetadata(tcpClient);

                    if (_Ssl)
                    {
                        if (AcceptInvalidCertificates)
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate));
                        }
                        else
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false);
                        }

                        bool success = await StartTls(client);

                        if (!success)
                        {
                            client.Dispose();
                            continue;
                        }
                    }

                    _Clients.TryAdd(clientIp, client);
                    _ClientsLastSeen.TryAdd(clientIp, DateTime.Now);
                    Logger?.Invoke("[SimpleTcp.Server] Starting data receiver for: " + clientIp);
                    ClientConnected?.Invoke(this, new ClientConnectedEventArgs(clientIp));
                    Task unawaited = Task.Run(() => DataReceiver(client), _Token);
                }
                catch (OperationCanceledException)
                {
                    return;
                }
                catch (ObjectDisposedException)
                {
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    continue;
                }
                catch (Exception e)
                {
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    Logger?.Invoke("[SimpleTcp.Server] Exception while awaiting connections: " + e.ToString());
                    continue;
                }
            }
        }
        private async Task DataReceiver(ClientMetadata client)
        {
            Logger?.Invoke(_Header + "data receiver started for client " + client.IpPort, TCPLogType.Debug);

            while (true)
            {
                try
                {
                    if (client.Token.IsCancellationRequested ||
                        !IsClientConnected(client.Client))
                    {
                        Logger?.Invoke(_Header + "client " + client.IpPort + " disconnected", TCPLogType.Debug);
                        break;
                    }

                    if (client.Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(_Header + "cancellation requested (data receiver for client " + client.IpPort + ")", TCPLogType.Debug);
                        break;
                    }

                    byte[] m_Buffer = await DataReadAsync(client);

                    if (m_Buffer == null)
                    {
                        await Task.Delay(30);

                        continue;
                    }

                    int BufferStart = 0;
                    int nBytesRec   = m_Buffer.Length;

                    // We support the PROXY protocol (currently v1)
                    // PROXY TCP4 192.168.0.37 192.168.0.121 57307 16248\r\n
                    // PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535\r\n
                    // \x0D \x0A \x0D \x0A \x00 \x0D \x0A \x51 \x55 \x49 \x54 \x0A
                    // Min 32 bytes, max 108 bytes
                    if (nBytesRec > 32 && m_Buffer[0] == 'P' && m_Buffer[1] == 'R' && m_Buffer[2] == 'O' && m_Buffer[3] == 'X' && m_Buffer[4] == 'Y' && m_Buffer[5] == ' ')
                    {
                        try
                        {
                            string   msg        = Encoding.UTF8.GetString(m_Buffer, 0, Math.Min(108, nBytesRec)); // 108 is the max we need to parse
                            string[] proxy      = msg.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
                            string[] proxyparts = proxy[0].Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);

                            client.OriginalIP       = IPAddress.Parse(proxyparts[2]);
                            client.OriginalSrcPort  = int.Parse(proxyparts[4]);
                            client.OriginalDestPort = int.Parse(proxyparts[5]);

                            client.IsProxied = true;

                            int split = msg.IndexOf("\r\n");

                            if (split > 32)
                            {
                                nBytesRec  -= split + 2;
                                BufferStart = split + 2;
                            }
                        }
                        catch (Exception)
                        {
                        }
                    }

                    byte[] package = new byte[nBytesRec];
                    Buffer.BlockCopy(m_Buffer, BufferStart, package, 0, nBytesRec);

                    var args = new DataReceivedFromClientEventArgs(client.IpPort, package, client.IncomingProtocol, client.Client.Client.RemoteEndPoint);
                    args.Data     = package;
                    args.Protocol = client.IncomingProtocol;

                    if (client.IsWebSocket)
                    {
                        args.WebSocketFrame = DecodeWebSocketMessage(package);

                        if (args.WebSocketFrame.OpCode == 0x9)
                        {
                            package[0] = (byte)((package[0] & 0xF0) + 0xA);
                            Send(client.IpPort, package);

                            return;
                        }
                    }

                    if (client.IsProxied)
                    {
                        args.OriginalIP       = client.OriginalIP;
                        args.OriginalSrcPort  = client.OriginalSrcPort;
                        args.OriginalDestPort = client.OriginalDestPort;
                    }

                    _Events.HandleDataReceived(this, args);
                    _Statistics.ReceivedBytes += package.Length;
                    UpdateClientLastSeen(client.IpPort);
                }
                catch (SocketException)
                {
                    Logger?.Invoke(_Header + "data receiver socket exception (disconnection) for " + client.IpPort, TCPLogType.Debug);
                }
                catch (Exception e)
                {
                    Logger?.Invoke(_Header + "data receiver exception for client " + client.IpPort + ":" +
                                   Environment.NewLine +
                                   e.ToString() +
                                   Environment.NewLine, TCPLogType.Warn);

                    break;
                }
            }

            Logger?.Invoke(_Header + "data receiver terminated for client " + client.IpPort, TCPLogType.Debug);

            if (_ClientsKicked.ContainsKey(client.IpPort))
            {
                _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Kicked));
            }
            else if (_ClientsTimedout.ContainsKey(client.IpPort))
            {
                _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Timeout));
            }
            else
            {
                _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Normal));
            }

            DateTime removedTs;

            _Clients.TryRemove(client.IpPort, out ClientMetadata destroyed);
            _ClientsLastSeen.TryRemove(client.IpPort, out removedTs);
            _ClientsKicked.TryRemove(client.IpPort, out removedTs);
            _ClientsTimedout.TryRemove(client.IpPort, out removedTs);
            client.Dispose();
        }
        private async Task DataReceiver(ClientMetadata client)
        {
            Logger?.Invoke(_Header + "data receiver started for client " + client.IpPort);

            CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(_Token, client.Token);

            while (true)
            {
                try
                {
                    if (!IsClientConnected(client.Client))
                    {
                        Logger?.Invoke(_Header + "client " + client.IpPort + " disconnected");
                        break;
                    }

                    if (client.Token.IsCancellationRequested)
                    {
                        Logger?.Invoke(_Header + "cancellation requested (data receiver for client " + client.IpPort + ")");
                        break;
                    }

                    byte[] data = await DataReadAsync(client, linkedCts.Token).ConfigureAwait(false);

                    if (data == null)
                    {
                        await Task.Delay(10).ConfigureAwait(false);

                        continue;
                    }

                    Task unawaited = Task.Run(() => _Events.HandleDataReceived(this, new DataReceivedFromClientEventArgs(client.IpPort, data)), linkedCts.Token);
                    _Statistics.ReceivedBytes += data.Length;
                    UpdateClientLastSeen(client.IpPort);
                }
                catch (IOException)
                {
                    Logger?.Invoke(_Header + "data receiver canceled, peer disconnected [" + client.IpPort + "]");
                }
                catch (SocketException)
                {
                    Logger?.Invoke(_Header + "data receiver canceled, peer disconnected [" + client.IpPort + "]");
                }
                catch (TaskCanceledException)
                {
                    Logger?.Invoke(_Header + "data receiver task canceled [" + client.IpPort + "]");
                }
                catch (ObjectDisposedException)
                {
                    Logger?.Invoke(_Header + "data receiver canceled due to disposal [" + client.IpPort + "]");
                }
                catch (Exception e)
                {
                    Logger?.Invoke(_Header + "data receiver exception [" + client.IpPort + "]:" +
                                   Environment.NewLine +
                                   e.ToString() +
                                   Environment.NewLine);

                    break;
                }
            }

            Logger?.Invoke(_Header + "data receiver terminated for client " + client.IpPort);

            if (_ClientsKicked.ContainsKey(client.IpPort))
            {
                _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Kicked));
            }
            else if (_ClientsTimedout.ContainsKey(client.IpPort))
            {
                _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Timeout));
            }
            else
            {
                _Events.HandleClientDisconnected(this, new ClientDisconnectedEventArgs(client.IpPort, DisconnectReason.Normal));
            }

            DateTime removedTs;

            _Clients.TryRemove(client.IpPort, out ClientMetadata destroyed);
            _ClientsLastSeen.TryRemove(client.IpPort, out removedTs);
            _ClientsKicked.TryRemove(client.IpPort, out removedTs);
            _ClientsTimedout.TryRemove(client.IpPort, out removedTs);
            client.Dispose();
        }
        private async Task AcceptConnections()
        {
            _IsListening = true;
            _Listener.Start();

            while (!_Token.IsCancellationRequested)
            {
                ClientMetadata client = null;

                try
                {
                    System.Net.Sockets.TcpClient tcpClient = await _Listener.AcceptTcpClientAsync().ConfigureAwait(false);

                    string clientIp = tcpClient.Client.RemoteEndPoint.ToString();

                    client = new ClientMetadata(tcpClient);

                    if (_Ssl)
                    {
                        if (_Settings.AcceptInvalidCertificates)
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate));
                        }
                        else
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false);
                        }

                        bool success = await StartTls(client).ConfigureAwait(false);

                        if (!success)
                        {
                            client.Dispose();
                            continue;
                        }
                    }

                    _Clients.TryAdd(clientIp, client);
                    _ClientsLastSeen.TryAdd(clientIp, DateTime.Now);
                    Logger?.Invoke(_Header + "starting data receiver for: " + clientIp);
                    _Events.HandleClientConnected(this, new ClientConnectedEventArgs(clientIp));

                    CancellationTokenSource linkedCts = CancellationTokenSource.CreateLinkedTokenSource(client.Token, _Token);
                    Task unawaited = Task.Run(() => DataReceiver(client), linkedCts.Token);
                }
                catch (TaskCanceledException)
                {
                    _IsListening = false;
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    return;
                }
                catch (OperationCanceledException)
                {
                    _IsListening = false;
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    return;
                }
                catch (ObjectDisposedException)
                {
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    continue;
                }
                catch (Exception e)
                {
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    Logger?.Invoke(_Header + "exception while awaiting connections: " + e.ToString());
                    continue;
                }
            }

            _IsListening = false;
        }
        private async Task SendInternalAsync(string ipPort, long contentLength, Stream stream, CancellationToken token)
        {
            ClientMetadata client = null;

            try
            {
                if (!_Clients.TryGetValue(ipPort, out client))
                {
                    return;
                }
                if (client == null)
                {
                    return;
                }

                long   bytesRemaining = contentLength;
                int    bytesRead      = 0;
                byte[] buffer         = new byte[_Settings.StreamBufferSize];

                await client.SendLock.WaitAsync(token).ConfigureAwait(false);

                while (bytesRemaining > 0)
                {
                    bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false);

                    if (bytesRead > 0)
                    {
                        if (!_Ssl)
                        {
                            await client.NetworkStream.WriteAsync(buffer, 0, bytesRead, token).ConfigureAwait(false);
                        }
                        else
                        {
                            await client.SslStream.WriteAsync(buffer, 0, bytesRead, token).ConfigureAwait(false);
                        }

                        bytesRemaining        -= bytesRead;
                        _Statistics.SentBytes += bytesRead;
                    }
                }

                if (!_Ssl)
                {
                    await client.NetworkStream.FlushAsync(token).ConfigureAwait(false);
                }
                else
                {
                    await client.SslStream.FlushAsync(token).ConfigureAwait(false);
                }
            }
            catch (TaskCanceledException)
            {
            }
            catch (OperationCanceledException)
            {
            }
            finally
            {
                if (client != null)
                {
                    client.SendLock.Release();
                }
            }
        }
        private async Task DataReceiver(ClientMetadata client)
        {
            string header = "[" + client.IpPort + "]";

            Log(header + " data receiver started");

            while (true)
            {
                try
                {
                    if (!IsClientConnected(client.Client))
                    {
                        Log(header + " client no longer connected");
                        break;
                    }

                    if (client.Token.IsCancellationRequested)
                    {
                        Log(header + " cancellation requested");
                        break;
                    }

                    byte[] data = await DataReadAsync(client);

                    if (data == null)
                    {
                        await Task.Delay(30);

                        continue;
                    }

                    if (DataReceived != null)
                    {
                        Task unawaited = Task.Run(() => DataReceived(client.IpPort, data));
                    }

                    UpdateClientLastSeen(client.IpPort);
                }
                catch (Exception e)
                {
                    Log(
                        Environment.NewLine +
                        header + " data receiver exception:" +
                        Environment.NewLine +
                        e.ToString() +
                        Environment.NewLine);

                    break;
                }
            }

            Log(header + " data receiver terminated");

            if (ClientDisconnected != null)
            {
                Task unawaited = null;

                if (_ClientsKicked.ContainsKey(client.IpPort))
                {
                    unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Kicked));
                }
                else if (_ClientsTimedout.ContainsKey(client.IpPort))
                {
                    unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Timeout));
                }
                else
                {
                    unawaited = Task.Run(() => ClientDisconnected(client.IpPort, DisconnectReason.Normal));
                }
            }

            DateTime removedTs;

            _Clients.TryRemove(client.IpPort, out ClientMetadata destroyed);
            _ClientsLastSeen.TryRemove(client.IpPort, out removedTs);
            _ClientsKicked.TryRemove(client.IpPort, out removedTs);
            _ClientsTimedout.TryRemove(client.IpPort, out removedTs);

            client.Dispose();
        }
        private async void AcceptConnections()
        {
            while (!_Token.IsCancellationRequested)
            {
                ClientMetadata client = null;

                try
                {
                    System.Net.Sockets.TcpClient tcpClient = await _Listener.AcceptTcpClientAsync();

                    string clientIp = tcpClient.Client.RemoteEndPoint.ToString();

                    client = new ClientMetadata(tcpClient);

                    if (_Ssl)
                    {
                        if (AcceptInvalidCertificates)
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false, new RemoteCertificateValidationCallback(AcceptCertificate));
                        }
                        else
                        {
                            client.SslStream = new SslStream(client.NetworkStream, false);
                        }

                        bool success = await StartTls(client);

                        if (!success)
                        {
                            client.Dispose();
                            continue;
                        }
                    }

                    _Clients.TryAdd(clientIp, client);
                    _ClientsLastSeen.TryAdd(clientIp, DateTime.Now);

                    Log("[" + clientIp + "] starting data receiver");

                    if (ClientConnected != null)
                    {
                        await Task.Run(() => ClientConnected(clientIp));
                    }

                    Task dataRecv = Task.Run(() => DataReceiver(client), _Token);
                }
                catch (OperationCanceledException)
                {
                    return;
                }
                catch (ObjectDisposedException)
                {
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    continue;
                }
                catch (Exception e)
                {
                    if (client != null)
                    {
                        client.Dispose();
                    }
                    Log("*** AcceptConnections exception: " + e.ToString());
                    continue;
                }
                finally
                {
                }
            }
        }