Пример #1
0
        /// <summary>
        /// Adds connected client to the list of clients.
        /// </summary>
        /// <param name="client">Client to add.</param>
        private void AddConnectedClient(NetworkPeerClient client)
        {
            this.logger.LogTrace("({0}.{1}:{2})", nameof(client), nameof(client.Id), client.Id);

            this.clientsById.AddOrReplace(client.Id, client);

            this.logger.LogTrace("(-)");
        }
        /// <summary>
        /// Adds connected client to the list of clients.
        /// </summary>
        /// <param name="client">Client to add.</param>
        private void AddConnectedClient(NetworkPeerClient client)
        {
            this.logger.LogTrace("({0}.{1}:{2})", nameof(client), nameof(client.Id), client.Id);

            this.clientsById.AddOrReplace(client.Id, client);
            client.ProcessingCompletion.Task.ContinueWith(unused => this.RemoveConnectedClient(client));

            this.logger.LogTrace("(-)");
        }
        /// <summary>
        /// Initializes an instance of the object.
        /// </summary>
        /// <param name="peer">Network peer the node is connected to.</param>
        /// <param name="client">Connected network client to the peer.</param>
        /// <param name="dateTimeProvider">Provider of time functions.</param>
        /// <param name="loggerFactory">Factory for creating loggers.</param>
        public NetworkPeerConnection(NetworkPeer peer, NetworkPeerClient client, IDateTimeProvider dateTimeProvider, ILoggerFactory loggerFactory)
        {
            this.logger           = loggerFactory.CreateLogger(this.GetType().FullName, $"[{peer.PeerAddress.Endpoint}] ");
            this.dateTimeProvider = dateTimeProvider;

            this.Peer     = peer;
            this.Client   = client;
            this.Messages = new BlockingCollection <SentMessage>(new ConcurrentQueue <SentMessage>());
        }
Пример #4
0
        /// <summary>
        /// Implements loop accepting connections from newly connected clients.
        /// </summary>
        private async Task AcceptClientsAsync()
        {
            this.logger.LogTrace("()");

            this.logger.LogTrace("Accepting incoming connections.");

            try
            {
                while (!this.serverCancel.IsCancellationRequested)
                {
                    // Used to record any errors occurring in the thread pool task.
                    Exception error = null;

                    TcpClient tcpClient = await Task.Run(() =>
                    {
                        try
                        {
                            Task <TcpClient> acceptTask = this.tcpListener.AcceptTcpClientAsync();
                            acceptTask.Wait(this.serverCancel.Token);
                            return(acceptTask.Result);
                        }
                        catch (Exception e)
                        {
                            // Record the error.
                            error = e;
                            return(null);
                        }
                    }).ConfigureAwait(false);

                    // Raise the error.
                    if (error != null)
                    {
                        throw error;
                    }

                    NetworkPeerClient client = this.networkPeerFactory.CreateNetworkPeerClient(tcpClient);

                    this.AddConnectedClient(client);

                    this.logger.LogTrace("Connection accepted from client '{0}'.", client.RemoteEndPoint);

                    // This should be cheaper for the accept loop thread than just calling ProcessNewClientAsync without awaiting.
                    Task unused = Task.Run(async() => await this.ProcessNewClientAsync(client));
                }
            }
            catch (OperationCanceledException)
            {
                this.logger.LogDebug("Shutdown detected, stop accepting connections.");
            }
            catch (Exception e)
            {
                this.logger.LogDebug("Exception occurred: {0}", e.ToString());
            }

            this.logger.LogTrace("(-)");
        }
        /// <summary>
        /// Removes a client from the list of clients and disconnects it.
        /// </summary>
        /// <param name="client">Client to remove and disconnect.</param>
        private void RemoveConnectedClient(NetworkPeerClient client)
        {
            this.logger.LogTrace("({0}.{1}:{2})", nameof(client), nameof(client.Id), client.Id);

            if (!this.clientsById.TryRemove(client.Id, out NetworkPeerClient unused))
            {
                this.logger.LogError("Internal data integration error.");
            }

            this.logger.LogTrace("(-)");
        }
Пример #6
0
        /// <summary>
        /// Initializes an instance of the object.
        /// </summary>
        /// <param name="peer">Network peer the node is connected to.</param>
        /// <param name="client">Connected network client to the peer.</param>
        /// <param name="dateTimeProvider">Provider of time functions.</param>
        /// <param name="loggerFactory">Factory for creating loggers.</param>
        public NetworkPeerConnection(NetworkPeer peer, NetworkPeerClient client, IDateTimeProvider dateTimeProvider, ILoggerFactory loggerFactory)
        {
            this.logger           = loggerFactory.CreateLogger(this.GetType().FullName, $"[{peer.PeerAddress.Endpoint}] ");
            this.dateTimeProvider = dateTimeProvider;

            this.Peer               = peer;
            this.Client             = client;
            this.Cancel             = new CancellationTokenSource();
            this.cancelRegistration = this.Cancel.Token.Register(this.InitiateShutdown);
            this.Disconnected       = new ManualResetEvent(false);
        }
Пример #7
0
        /// <summary>
        /// Initializes an instance of the object for outbound network peers.
        /// </summary>
        /// <param name="peerAddress">Information about the peer including its network address, protocol version, time of last contact.</param>
        /// <param name="network">Specification of the network the node runs on - regtest/testnet/mainnet.</param>
        /// <param name="parameters">Various settings and requirements related to how the connections with peers are going to be established, or <c>null</c> to use default parameters.</param>
        /// <param name="networkPeerFactory">Factory for creating P2P network peers.</param>
        /// <param name="dateTimeProvider">Provider of time functions.</param>
        /// <param name="loggerFactory">Factory for creating loggers.</param>
        public NetworkPeer(NetworkAddress peerAddress, Network network, NetworkPeerConnectionParameters parameters, INetworkPeerFactory networkPeerFactory, IDateTimeProvider dateTimeProvider, ILoggerFactory loggerFactory)
            : this(false, peerAddress, network, parameters, dateTimeProvider, loggerFactory)
        {
            NetworkPeerClient client = networkPeerFactory.CreateNetworkPeerClient(parameters);

            this.logger = loggerFactory.CreateLogger(this.GetType().FullName, $"[{client.Id}-{peerAddress.Endpoint}] ");
            this.logger.LogTrace("()");

            this.Connection = new NetworkPeerConnection(this, client, this.dateTimeProvider, this.loggerFactory);

            this.logger.LogTrace("(-)");
        }
Пример #8
0
        /// <summary>
        /// Initializes an instance of the object for outbound network peers.
        /// </summary>
        /// <param name="peerAddress">Information about the peer including its network address, protocol version, time of last contact.</param>
        /// <param name="network">Specification of the network the node runs on - regtest/testnet/mainnet.</param>
        /// <param name="parameters">Various settings and requirements related to how the connections with peers are going to be established, or <c>null</c> to use default parameters.</param>
        /// <param name="networkPeerFactory">Factory for creating P2P network peers.</param>
        /// <param name="dateTimeProvider">Provider of time functions.</param>
        /// <param name="loggerFactory">Factory for creating loggers.</param>
        public NetworkPeer(NetworkAddress peerAddress, Network network, NetworkPeerConnectionParameters parameters, INetworkPeerFactory networkPeerFactory, IDateTimeProvider dateTimeProvider, ILoggerFactory loggerFactory)
            : this(false, peerAddress, network, parameters, dateTimeProvider, loggerFactory)
        {
            this.logger.LogTrace("()");

            NetworkPeerClient client = networkPeerFactory.CreateNetworkPeerClient(parameters);

            this.Connection = new NetworkPeerConnection(this, client, this.dateTimeProvider, this.loggerFactory);
            this.ConnectAsync(parameters.ConnectCancellation).GetAwaiter().GetResult();

            this.InitDefaultBehaviors(parameters);
            this.Connection.StartReceiveMessages();

            this.logger.LogTrace("(-)");
        }
Пример #9
0
        /// <summary>
        /// Removes a client from the list of clients and disconnects it.
        /// </summary>
        /// <param name="client">Client to remove and disconnect.</param>
        private void RemoveAndDisconnectConnectedClient(NetworkPeerClient client)
        {
            this.logger.LogTrace("({0}.{1}:{2})", nameof(client), nameof(client.Id), client.Id);

            if (!this.clientsById.TryRemove(client.Id, out NetworkPeerClient unused))
            {
                this.logger.LogError("Internal data integration error.");
            }

            TaskCompletionSource <bool> completion = client.ProcessingCompletion;

            client.Dispose();
            completion.SetResult(true);

            this.logger.LogTrace("(-)");
        }
Пример #10
0
        /// <summary>
        /// Implements loop accepting connections from newly connected clients.
        /// </summary>
        private async Task AcceptClientsAsync()
        {
            this.logger.LogTrace("()");

            this.logger.LogTrace("Accepting incoming connections.");

            while (!this.serverCancel.IsCancellationRequested)
            {
                try
                {
                    this.serverCancel.Token.ThrowIfCancellationRequested();

                    TcpClient tcpClient = await Task.Run(async() => await this.tcpListener.AcceptTcpClientAsync(), this.serverCancel.Token);

                    NetworkPeerClient client = this.networkPeerFactory.CreateNetworkPeerClient(tcpClient);

                    this.AddConnectedClient(client);

                    this.logger.LogTrace("Connection accepted from client '{0}'.", client.RemoteEndPoint);
                    Task unused = Task.Run(() => this.ProcessNewClient(client));
                }
                catch (Exception e)
                {
                    if (e is OperationCanceledException)
                    {
                        this.logger.LogDebug("Shutdown detected, stop accepting connections.");
                    }
                    else
                    {
                        this.logger.LogDebug("Exception occurred: {0}");
                    }

                    break;
                }
            }

            this.logger.LogTrace("(-)");
        }
        /// <summary>
        /// Implements loop accepting connections from newly connected clients.
        /// </summary>
        private async Task AcceptClientsAsync()
        {
            this.logger.LogTrace("()");

            this.logger.LogTrace("Accepting incoming connections.");

            try
            {
                while (!this.serverCancel.IsCancellationRequested)
                {
                    // Used to record any errors occurring in the thread pool task.
                    Exception error = null;

                    TcpClient tcpClient = await Task.Run(() =>
                    {
                        try
                        {
                            Task <TcpClient> acceptTask = this.tcpListener.AcceptTcpClientAsync();
                            acceptTask.Wait(this.serverCancel.Token);
                            return(acceptTask.Result);
                        }
                        catch (Exception e)
                        {
                            // Record the error.
                            error = e;
                            return(null);
                        }
                    }).ConfigureAwait(false);

                    // Raise the error.
                    if (error != null)
                    {
                        throw error;
                    }

                    NetworkPeerClient client = this.networkPeerFactory.CreateNetworkPeerClient(tcpClient);

                    this.logger.LogTrace("Connection accepted from client '{0}'.", client.RemoteEndPoint);

                    var peerAddress = new NetworkAddress()
                    {
                        Endpoint = client.RemoteEndPoint,
                        Time     = this.dateTimeProvider.GetUtcNow()
                    };

                    NetworkPeer networkPeer = this.networkPeerFactory.CreateNetworkPeer(peerAddress, this.Network, client, this.CreateNetworkPeerConnectionParameters());

                    this.ConnectedNetworkPeers.Add(networkPeer);
                    networkPeer.StateChanged += this.Peer_StateChanged;

                    this.AddConnectedClient(client);
                }
            }
            catch (OperationCanceledException)
            {
                this.logger.LogDebug("Shutdown detected, stop accepting connections.");
            }
            catch (Exception e)
            {
                this.logger.LogDebug("Exception occurred: {0}", e.ToString());
            }

            this.logger.LogTrace("(-)");
        }
Пример #12
0
        /// <summary>
        /// Initializes an instance of the object for inbound network peers with already established connection.
        /// </summary>
        /// <param name="peerAddress">Information about the peer including its network address, protocol version, time of last contact.</param>
        /// <param name="network">Specification of the network the node runs on - regtest/testnet/mainnet.</param>
        /// <param name="parameters">Various settings and requirements related to how the connections with peers are going to be established, or <c>null</c> to use default parameters.</param>
        /// <param name="client">Already connected network client.</param>
        /// <param name="peerVersion">Version message payload received from the peer.</param>
        /// <param name="dateTimeProvider">Provider of time functions.</param>
        /// <param name="loggerFactory">Factory for creating loggers.</param>
        public NetworkPeer(NetworkAddress peerAddress, Network network, NetworkPeerConnectionParameters parameters, NetworkPeerClient client, VersionPayload peerVersion, IDateTimeProvider dateTimeProvider, ILoggerFactory loggerFactory)
            : this(true, peerAddress, network, parameters, dateTimeProvider, loggerFactory)
        {
            this.logger.LogTrace("()");

            this.RemoteSocketEndpoint = client.RemoteEndPoint;
            this.RemoteSocketAddress  = this.RemoteSocketEndpoint.Address;
            this.RemoteSocketPort     = this.RemoteSocketEndpoint.Port;

            this.PeerVersion = peerVersion;
            this.Connection  = new NetworkPeerConnection(this, client, this.dateTimeProvider, this.loggerFactory);
            this.ConnectedAt = this.dateTimeProvider.GetUtcNow();

            this.logger.LogTrace("Connected to advertised node '{0}'.", this.PeerAddress.Endpoint);
            this.State = NetworkPeerState.Connected;

            this.InitDefaultBehaviors(parameters);
            this.Connection.StartReceiveMessages();

            this.logger.LogTrace("(-)");
        }
 /// <inheritdoc/>
 public NetworkPeer CreateNetworkPeer(NetworkAddress peerAddress, Network network, NetworkPeerConnectionParameters parameters, NetworkPeerClient client, VersionPayload peerVersion)
 {
     return(new NetworkPeer(peerAddress, network, parameters, client, peerVersion, this.dateTimeProvider, this.loggerFactory));
 }
Пример #14
0
        /// <summary>
        /// Handles a newly accepted client's connection.
        /// </summary>
        /// <param name="client">Newly accepted client.</param>
        private async Task ProcessNewClientAsync(NetworkPeerClient client)
        {
            this.logger.LogTrace("({0}:{1})", nameof(client), client.RemoteEndPoint);

            bool keepClientConnected = false;

            EndPoint clientEndPoint = client.RemoteEndPoint;

            try
            {
                if (this.ConnectedNetworkPeers.Count < this.MaxConnections)
                {
                    using (var cancel = CancellationTokenSource.CreateLinkedTokenSource(this.serverCancel.Token))
                    {
                        cancel.CancelAfter(TimeSpan.FromSeconds(10));

                        while (true)
                        {
                            cancel.Token.ThrowIfCancellationRequested();
                            Message message = await client.ReadAndParseMessageAsync(this.Version, cancel.Token).ConfigureAwait(false);

                            this.messageProducer.PushMessage(new IncomingMessage()
                            {
                                Client      = client,
                                Message     = message,
                                Length      = message.MessageSize,
                                NetworkPeer = null,
                            });

                            if (message.Payload is VersionPayload)
                            {
                                this.logger.LogTrace("Connection with client '{0}' successfully initiated.", client.RemoteEndPoint);
                                keepClientConnected = true;
                                break;
                            }

                            this.logger.LogTrace("The first message of the remote peer '{0}' did not contain a version payload.", client.RemoteEndPoint);
                        }
                    }
                }
                else
                {
                    this.logger.LogDebug("Maximum number of connections {0} reached, client '{1}' will be disconnected.", this.MaxConnections, clientEndPoint);
                }
            }
            catch (OperationCanceledException)
            {
                if (this.serverCancel.Token.IsCancellationRequested)
                {
                    this.logger.LogTrace("Shutdown detected.");
                }
                else
                {
                    this.logger.LogTrace("Inbound client '{0}' failed to send a version message within 10 seconds, dropping connection.", client.RemoteEndPoint);
                }
            }
            catch (Exception ex)
            {
                this.logger.LogTrace("Exception occurred while processing message from client '{0}': {1}", client.RemoteEndPoint, ex.ToString());
            }

            if (!keepClientConnected)
            {
                // NetworkPeer is not created for this client here yet.
                // This is why we need to finish its completion.
                client.Dispose();
                client.ProcessingCompletion.SetResult(true);
            }

            this.logger.LogTrace("(-)");
        }
Пример #15
0
        /// <inheritdoc/>
        public NetworkPeer CreateNetworkPeer(NetworkAddress peerAddress, Network network, NetworkPeerClient client, VersionPayload peerVersion, NetworkPeerConnectionParameters parameters = null)
        {
            Guard.NotNull(peerAddress, nameof(peerAddress));
            Guard.NotNull(network, nameof(network));
            Guard.NotNull(client, nameof(client));
            Guard.NotNull(client.RemoteEndPoint, $"{nameof(client)}.{nameof(client.RemoteEndPoint)}");

            return(new NetworkPeer(peerAddress, network, parameters, client, peerVersion, this.dateTimeProvider, this.loggerFactory));
        }
Пример #16
0
        /// <summary>
        /// Handles a newly accepted client's connection.
        /// </summary>
        /// <param name="client">Newly accepted client.</param>
        private void ProcessNewClient(NetworkPeerClient client)
        {
            this.logger.LogTrace("({0}:{1})", nameof(client), client.RemoteEndPoint);

            bool keepClientConnected = false;

            EndPoint clientEndPoint = client.RemoteEndPoint;

            try
            {
                this.serverCancel.Token.ThrowIfCancellationRequested();

                if (this.ConnectedNetworkPeers.Count < this.MaxConnections)
                {
                    using (var cancel = CancellationTokenSource.CreateLinkedTokenSource(this.serverCancel.Token))
                    {
                        cancel.CancelAfter(TimeSpan.FromSeconds(10));

                        while (true)
                        {
                            cancel.Token.ThrowIfCancellationRequested();

                            PerformanceCounter counter;
                            Message            message = Message.ReadNext(client.Stream, this.Network, this.Version, cancel.Token, out counter);
                            this.messageProducer.PushMessage(new IncomingMessage()
                            {
                                Client      = client,
                                Message     = message,
                                Length      = counter.ReadBytes,
                                NetworkPeer = null,
                            });

                            if (message.Payload is VersionPayload)
                            {
                                this.logger.LogTrace("Connection with client '{0}' successfully initiated.", client.RemoteEndPoint);
                                keepClientConnected = true;
                                break;
                            }

                            this.logger.LogTrace("The first message of the remote peer '{0}' did not contain a version payload.", client.RemoteEndPoint);
                        }
                    }
                }
                else
                {
                    this.logger.LogDebug("Maximum number of connections {0} reached, client '{1}' will be disconnected.", this.MaxConnections, clientEndPoint);
                }
            }
            catch (OperationCanceledException)
            {
                if (this.serverCancel.Token.IsCancellationRequested)
                {
                    this.logger.LogTrace("Shutdown detected.");
                }
                else
                {
                    this.logger.LogTrace("Inbound client '{0}' failed to send a message within 10 seconds, dropping connection.", client.RemoteEndPoint);
                }
            }
            catch (Exception ex)
            {
                this.logger.LogTrace("Exception occurred while processing message from client '{0}': {1}", client.RemoteEndPoint, ex.ToString());
            }

            if (!keepClientConnected)
            {
                this.RemoveAndDisconnectConnectedClient(client);
            }

            this.logger.LogTrace("(-)");
        }