Example #1
0
        /// <summary>
        /// Thread procedure that is responsible for accepting new clients on the TCP server port.
        /// New clients are put into clientQueue, from which they are consumed by clientQueueHandlerThread.
        /// </summary>
        private void AcceptThread()
        {
            LogDiagnosticContext.Start();

            log.Trace("()");

            acceptThreadFinished.Reset();

            AutoResetEvent acceptTaskEvent = new AutoResetEvent(false);

            while (!ShutdownSignaling.IsShutdown)
            {
                log.Debug("Waiting for new client.");
                Task <TcpClient> acceptTask = Listener.AcceptTcpClientAsync();
                acceptTask.ContinueWith(t => acceptTaskEvent.Set());

                WaitHandle[] handles = new WaitHandle[] { acceptTaskEvent, ShutdownSignaling.ShutdownEvent };
                int          index   = WaitHandle.WaitAny(handles);
                if (handles[index] == ShutdownSignaling.ShutdownEvent)
                {
                    log.Info("Shutdown detected.");
                    break;
                }

                try
                {
                    // acceptTask is finished here, asking for Result won't block.
                    TcpClient client = acceptTask.Result;
                    EndPoint  ep     = client.Client.RemoteEndPoint;
                    lock (clientQueueLock)
                    {
                        clientQueue.Enqueue(client);
                    }
                    log.Debug("New client '{0}' accepted.", ep);
                    clientQueueEvent.Set();
                }
                catch (Exception e)
                {
                    log.Error("Exception occurred: {0}", e.ToString());
                }
            }

            acceptThreadFinished.Set();

            log.Trace("(-)");

            LogDiagnosticContext.Stop();
        }
        /// <summary>
        /// Connects to a specific IP address and port and initializes stream.
        /// If TLS is used, client authentication is done as well.
        /// </summary>
        /// <returns>true if the connection was established succcessfully, false otherwise.</returns>
        public async Task <bool> ConnectAsync()
        {
            log.Trace("()");

            bool res = false;

            try
            {
                await TcpClient.ConnectAsync(remoteEndPoint.Address, remoteEndPoint.Port);

                stream = TcpClient.GetStream();
                if (UseTls)
                {
                    SslStream sslStream = new SslStream(stream, false, PeerCertificateValidationCallback);
                    await sslStream.AuthenticateAsClientAsync("", null, SslProtocols.Tls12, false);

                    stream = sslStream;
                }
                res = true;
            }
            catch (Exception e)
            {
                log.Debug("Unable to connect to {0}, error exception: {1}", remoteEndPoint, e.ToString());
            }


            log.Trace("(-):{0}", res);
            return(res);
        }
Example #3
0
        /// <summary>
        /// Thread procedure that consumes clients from clientQueue.
        /// When a new client is detected in the queue, it is removed from the queue
        /// and enters asynchronous read and processing loop.
        /// </summary>
        private void ClientQueueHandlerThread()
        {
            log.Info("()");

            clientQueueHandlerThreadFinished.Reset();

            while (!ShutdownSignaling.IsShutdown)
            {
                WaitHandle[] handles = new WaitHandle[] { clientQueueEvent, ShutdownSignaling.ShutdownEvent };
                int          index   = WaitHandle.WaitAny(handles);
                if (handles[index] == ShutdownSignaling.ShutdownEvent)
                {
                    log.Info("Shutdown detected.");
                    break;
                }

                log.Debug("New client in the queue detected, queue count is {0}.", clientQueue.Count);
                bool queueEmpty = false;
                while (!queueEmpty && !ShutdownSignaling.IsShutdown)
                {
                    TcpClient tcpClient = null;
                    lock (clientQueueLock)
                    {
                        if (clientQueue.Count > 0)
                        {
                            tcpClient = clientQueue.Peek();
                        }
                    }

                    if (tcpClient != null)
                    {
                        int keepAliveInterval = IsServingClientsOnly ? ClientKeepAliveIntervalSeconds : NodeKeepAliveIntervalSeconds;

                        Client client = new Client(this, tcpClient, clientList.GetNewClientId(), UseTls, keepAliveInterval);
                        ClientHandlerAsync(client);

                        lock (clientQueueLock)
                        {
                            clientQueue.Dequeue();
                            queueEmpty = clientQueue.Count == 0;
                        }
                    }
                    else
                    {
                        queueEmpty = true;
                    }
                }
            }

            clientQueueHandlerThreadFinished.Set();

            log.Info("(-)");
        }
Example #4
0
        /// <summary>
        /// Asynchronous read and processing function for each client that connects to the TCP server.
        /// </summary>
        /// <param name="Client">Client that is connected to TCP server.</param>
        /// <remarks>The client is being handled in the processing loop until the connection to it
        /// is terminated by either side. This function implements reading the message from the network stream,
        /// which includes reading the message length prefix followed by the entire message.</remarks>
        private async void ClientHandlerAsync(Client Client)
        {
            this.log.Info("(Client.RemoteEndPoint:{0})", Client.RemoteEndPoint);

            string       prefix = string.Format("{0}[{1}] ", this.logPrefix, Client.RemoteEndPoint);
            PrefixLogger log    = new PrefixLogger(this.logName, prefix);

            clientList.AddNetworkPeer(Client);
            log.Debug("Client ID set to 0x{0:X16}.", Client.Id);

            await Client.ReceiveMessageLoop();

            // Free resources used by the client.
            clientList.RemoveNetworkPeer(Client);
            await Client.HandleDisconnect();

            Client.Dispose();

            log.Info("(-)");
        }
        /// <summary>
        /// Callback routine that is called once the timeoutTimer expires.
        /// <para>
        /// If relay status is WaitingForCalleeResponse, the callee has to reply to the incoming call notification
        /// within a reasonable time. If it does the timer is cancelled. If it does not, the timeout occurs.
        /// </para>
        /// <para>
        /// If relay status is WaitingForFirstInitMessage, both clients are expected to connect to clAppService port
        /// and send an initial message over that service. The timeoutTimer expires when none of the clients
        /// connects to clAppService port and sends its initialization message within a reasonable time.
        /// </para>
        /// <para>
        /// Then if relay status is WaitingForSecondInitMessage, the node receives a message from the first client
        /// on clAppService port, it starts the timer again, which now expires if the second client does not connect
        /// and send its initial message within a reasonable time.
        /// </para>
        /// </summary>
        /// <param name="state">Status of the relay when the timer was installed.</param>
        private async void TimeoutCallback(object State)
        {
            RelayConnectionStatus previousStatus = (RelayConnectionStatus)State;

            log.Trace("(State:{0})", previousStatus);

            Client  clientToSendMessage = null;
            Message messageToSend       = null;
            bool    destroyRelay        = false;

            await lockObject.WaitAsync();

            if (timeoutTimer != null)
            {
                switch (status)
                {
                case RelayConnectionStatus.WaitingForCalleeResponse:
                {
                    // The caller requested the call and the callee was notified.
                    // The callee failed to send us response on time, this is situation 2)
                    // from ProcessMessageCallIdentityApplicationServiceRequestAsync.
                    // We send ERROR_NOT_AVAILABLE to the caller and destroy the relay.
                    log.Debug("Callee failed to reply to the incoming call notification, closing relay.");

                    clientToSendMessage = caller;
                    messageToSend       = caller.MessageBuilder.CreateErrorNotAvailableResponse(pendingMessage);
                    break;
                }

                case RelayConnectionStatus.WaitingForFirstInitMessage:
                {
                    // Neither client joined the channel on time, nothing to do, just destroy the relay.
                    log.Debug("None of the clients joined the relay on time, closing relay.");
                    break;
                }

                case RelayConnectionStatus.WaitingForSecondInitMessage:
                {
                    // One client is waiting for the other one to join, but the other client failed to join on time.
                    // We send ERROR_NOT_FOUND to the waiting client and close its connection.
                    log.Debug("{0} failed to join the relay on time, closing relay.", callee != null ? "Caller" : "Callee");

                    clientToSendMessage = callee != null ? callee : caller;
                    messageToSend       = clientToSendMessage.MessageBuilder.CreateErrorNotFoundResponse(pendingMessage);
                    break;
                }

                default:
                    log.Debug("Time out triggered while the relay status was {0}.", status);
                    break;
                }

                // In case of any timeouts, we just destroy the relay.
                destroyRelay = true;
            }
            else
            {
                log.Debug("Timeout timer of relay '{0}' has been destroyed, no action taken.", id);
            }

            lockObject.Release();


            if (messageToSend != null)
            {
                if (!await clientToSendMessage.SendMessageAsync(messageToSend))
                {
                    log.Warn("Unable to send message to the client ID '0x{0:X16}' in relay '{1}', maybe it is not connected anymore.", clientToSendMessage.Id, id);
                }
            }

            if (destroyRelay)
            {
                Server     serverComponent = (Server)Base.ComponentDictionary["Network.Server"];
                ClientList clientList      = serverComponent.GetClientList();
                await clientList.DestroyNetworkRelay(this);
            }

            log.Trace("(-)");
        }