/// <summary> /// Thread that is responsible for connection to LOC and processing LOC updates. /// If the LOC is not reachable, the thread will wait until it is reachable. /// If connection to LOC is established and closed for any reason, the thread will try to reconnect. /// </summary> private async void LocConnectionThread() { LogDiagnosticContext.Start(); log.Info("()"); locConnectionThreadFinished.Reset(); try { while (!ShutdownSignaling.IsShutdown) { // Connect to LOC server. if (await client.ConnectAsync()) { // Announce our primary server interface to LOC. if (await client.RegisterPrimaryServerRoleAsync(Config.Configuration.ServerRoles.GetRolePort((uint)ServerRole.Primary), Iop.Locnet.ServiceType.Profile)) { // Ask LOC server about initial set of neighborhood nodes. if (await GetNeighborhoodInformationAsync()) { // Receive and process updates. await client.ReceiveMessageLoopAsync(); } await client.DeregisterPrimaryServerRoleAsync(); } } } } catch (Exception e) { if (!ShutdownSignaling.IsShutdown) { log.Error("Exception occurred (and rethrowing): {0}", e.ToString()); await Task.Delay(5000); throw e; } else { log.Debug("Shutdown invoked exception."); } } if (client != null) { client.Dispose(); } locConnectionThreadFinished.Set(); log.Info("(-)"); LogDiagnosticContext.Stop(); }
/// <summary> /// Thread that is responsible for late initialization of network servers. /// This is needed because in order for the proximity server to run, it must know its location. /// The location is received from LOC server, but there is no guarantee LOC server is running /// when the proximity server starts. /// </summary> private async void DelayedStartupThread() { LogDiagnosticContext.Start(); log.Info("()"); delayedStartupThreadFinished.Reset(); try { Console.WriteLine("Waiting for location initialization ..."); LocationBasedNetwork loc = (LocationBasedNetwork)Base.ComponentDictionary[LocationBasedNetwork.ComponentName]; bool locInit = false; try { locInit = await loc.LocLocationInitializedEvent.Task; } catch { // Catch cancellation exception. log.Trace("Shutdown detected."); } if (locInit) { log.Debug("LOC location is initialized, we can continue with Network server component initialization."); if (base.Init()) { RegisterCronJobs(); DelayedStartupCompletedEvent.TrySetResult(true); } else { log.Error("Delayed initialization failed, initiating shutdown."); Base.ComponentManager.GlobalShutdown.SignalShutdown(); } Console.WriteLine("Location initialization completed."); } } catch (Exception e) { log.Error("Exception occurred (and rethrowing): {0}", e.ToString()); await Task.Delay(5000); throw e; } delayedStartupThreadFinished.Set(); log.Info("(-)"); LogDiagnosticContext.Stop(); }
/// <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> /// Handler 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.</remarks> private async void ClientHandlerAsync(IncomingClientBase Client) { LogDiagnosticContext.Start(); log.Info("(Client.RemoteEndPoint:{0})", Client.RemoteEndPoint); clientList.AddNetworkPeer(Client); log.Debug("Client ID set to {0}.", Client.Id.ToHex()); await Client.ReceiveMessageLoop(); // Free resources used by the client. clientList.RemoveNetworkPeer(Client); await Client.HandleDisconnect(); Client.Dispose(); log.Info("(-)"); LogDiagnosticContext.Stop(); }
/// <summary> /// Handler for each client that connects to the TCP server. /// </summary> /// <param name="Client">Client that is connected to TCP server.</param> private async void ClientHandlerAsync(TcpClient Client) { LogDiagnosticContext.Start(); log.Debug("(Client.Client.RemoteEndPoint:{0})", Client.Client.RemoteEndPoint); connectedProfileServer = Client; connectedProfileServerMessageBuilder = new LocMessageBuilder(0, new List <SemVer>() { SemVer.V100 }); await ReceiveMessageLoop(Client, connectedProfileServerMessageBuilder); connectedProfileServerWantsUpdates = false; connectedProfileServer = null; Client.Dispose(); log.Debug("(-)"); LogDiagnosticContext.Stop(); }
/// <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 profile server 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) { LogDiagnosticContext.Start(); RelayConnectionStatus previousStatus = (RelayConnectionStatus)State; log.Trace("(State:{0})", previousStatus); IncomingClient clientToSendMessage = null; PsProtocolMessage 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 {0} in relay '{1}', maybe it is not connected anymore.", clientToSendMessage.Id.ToHex(), id); } } if (destroyRelay) { Server serverComponent = (Server)Base.ComponentDictionary[Server.ComponentName]; await serverComponent.RelayList.DestroyNetworkRelay(this); } log.Trace("(-)"); LogDiagnosticContext.Stop(); }