/// <summary> /// Called when a new server connects to this server. /// </summary> /// <param name="connection">The new connection.</param> internal void HandleNewConnection(NetworkServerConnection connection) { //TODO make configurable PendingDownstreamRemoteServer server = new PendingDownstreamRemoteServer(connection, 10000, HandleServerReady, HandleServerDroppedBeforeReady, logger); lock (pendingDownstreamServers) pendingDownstreamServers.Add(server); connection.StartListening(); logger.Trace($"New server connected, awaiting identification [{connection.RemoteEndPoints.Format()}]."); }
/// <summary> /// Creates a new remote server. /// </summary> /// <param name="connection">The connection to the server.</param> /// <param name="timeoutMs">The number of milliseconds to wait before timing out.</param> /// <param name="ready">Delegate invoked if the connection is dropped.</param> /// <param name="dropped">Delegate invoked if the remote server identifies itself.</param> /// <param name="logger">The logger to use.</param> internal PendingDownstreamRemoteServer(NetworkServerConnection connection, int timeoutMs, Action <PendingDownstreamRemoteServer, ushort> ready, Action <PendingDownstreamRemoteServer> dropped, Logger logger) { this.Connection = connection; this.Ready = ready; this.Dropped = dropped; this.logger = logger; connection.MessageReceived += MessageReceivedHandler; connection.Disconnected += DisconnectedHandler; // Wait until we get a message with their ID timer = new System.Threading.Timer((_) => DropConnection(), null, timeoutMs, Timeout.Infinite); }
/// <summary> /// Sets the connection being used by this remote server. /// </summary> /// <param name="pendingServer">The connection to switch to.</param> internal void SetConnection(PendingDownstreamRemoteServer pendingServer) { if (connection != null) { connection.MessageReceived -= MessageReceivedHandler; connection.Disconnected -= DisconnectedHandler; } connection = pendingServer.Connection; // Switch out message received handler from the pending server connection.MessageReceived = MessageReceivedHandler; connection.Disconnected = DisconnectedHandler; EventHandler <ServerConnectedEventArgs> handler = ServerConnected; if (handler != null) { void DoServerConnectedEvent() { long startTimestamp = Stopwatch.GetTimestamp(); try { handler?.Invoke(this, new ServerConnectedEventArgs(this)); } catch (Exception e) { serverConnectedEventFailuresCounter.Increment(); logger.Error("A plugin encountered an error whilst handling the ServerConnected event. The server will still be connected. (See logs for exception)", e); } double time = (double)(Stopwatch.GetTimestamp() - startTimestamp) / Stopwatch.Frequency; serverConnectedEventTimeHistogram.Report(time); } threadHelper.DispatchIfNeeded(DoServerConnectedEvent); } // Handle all messages that had queued foreach (PendingDownstreamRemoteServer.QueuedMessage queuedMessage in pendingServer.GetQueuedMessages()) { HandleMessage(queuedMessage.Message, queuedMessage.SendMode); queuedMessage.Message.Dispose(); } }
/// <summary> /// Registers a new connection to the server. /// </summary> /// <param name="connection">The new connection.</param> protected void RegisterConnection(NetworkServerConnection connection) { Action <NetworkServerConnection> handler = RegisteredConnection; if (handler != null) { handler.Invoke(connection); } else { Logger.Error("A connection was registered by the network listener while no hooks were subscribed to handle the registration. The connection has been dropped. This suggests the network listener is erroneously accepting connections before the StartListening() method has been called."); connection.Disconnect(); } }
/// <summary> /// Called when a new client connects. /// </summary> /// <param name="connection">The new client.</param> internal void HandleNewConnection(NetworkServerConnection connection) { //Allocate ID and add to list ushort id; try { id = ReserveID(); } catch (InvalidOperationException) { logger.Info($"New client could not be connected as there were no IDs available to allocate to them [{connection.RemoteEndPoints.Format()}]."); connection.Disconnect(); return; } Client client; try { client = Client.Create( connection, id, this, threadHelper, clientLogger #if PRO , clientMetricsCollector #endif ); } catch (Exception e) { logger.Error("An exception ocurred while connecting a client. The client has been dropped.", e); connection.Disconnect(); DeallocateID(id, out int _); return; } AllocateIDToClient(id, client, out int noClients); // TODO if a client sends immediately after connecting then the message will be missed as the Connected event has not yet fired connection.Client = client; logger.Info($"New client [{client.ID}] connected [{client.RemoteEndPoints.Format()}]."); #if PRO clientsConnectedGauge.Report(noClients); #endif //Inform plugins of the new connection EventHandler <ClientConnectedEventArgs> handler = ClientConnected; if (handler != null) { threadHelper.DispatchIfNeeded( delegate() { #if PRO long startTimestamp = Stopwatch.GetTimestamp(); #endif try { handler.Invoke(this, new ClientConnectedEventArgs(client)); } catch (Exception e) { logger.Error("A plugin encountered an error whilst handling the ClientConnected event. The client will be disconnected. (See logs for exception)", e); client.DropConnection(); #if PRO clientConnectedEventFailuresCounter.Increment(); #endif return; } #if PRO double time = (double)(Stopwatch.GetTimestamp() - startTimestamp) / Stopwatch.Frequency; clientConnectedEventTimeHistogram.Report(time); #endif }, (_) => client.StartListening() ); } }