Exemple #1
0
        public async Task CreateConnectionsAsync()
        {
            try
            {
                while (!this.cancellation.Token.IsCancellationRequested)
                {
                    while (this.connectedPeers.Count < 32 && !this.cancellation.Token.IsCancellationRequested)
                    {
                        ConnectedPeer
                            nextPeer =
                            await CreateNextPeerAsync();     // this method must block and only run on one thread, it's not thread safe


                        if (nextPeer == null)
                        {
                            this.logger.LogInformation("Out off connection candidates...");
                            break;
                        }

                        _ = Task.Run(() =>
                                     ConnectAndRunAsync(
                                         nextPeer)); // Connecting and running happens on max X threads, so that we can build up connections quickly
                    }

                    this.logger.LogInformation("Waiting 30 seconds..");
                    await Task.Delay(30000, this.cancellation.Token).ContinueWith(_ => { }); // we do not want to have a TaskCancelledException here
                }
                DisconnectAsync();
            }
            catch (Exception e)
            {
                this.logger.LogCritical(e.Message);
                this.FaultReason = e;
            }
        }
Exemple #2
0
        async Task HandleFailedConnectedPeerAsync(Exception e, ConnectedPeer connection)
        {
            if (connection == null)  // there was no connection available
            {
                return;
            }

            if (e == null) // explicit disconnect
            {
                Debug.Assert(connection.PeerState.HasFlag(ConnectedPeer.State.Disconnecting));
                Debug.Assert(connection.PeerState.HasFlag(ConnectedPeer.State.Disposed));
            }
            else
            {
                Debug.Assert(connection.PeerState.HasFlag(ConnectedPeer.State.Failed));
                Debug.Assert(connection.PeerState.HasFlag(ConnectedPeer.State.Disposed));
            }


            if (PeerManager.ShouldRecordError(e, this.cancellation.Token.IsCancellationRequested, connection.ToString(), this.logger))
            {
                // set these properties on the loaded connection instance and not only in the repository, so that we can
                // use the cached collection of Peer objects
                connection.RemotePeer.LastError = DateTime.UtcNow;
                await this.peerRepository.UpdatePeerLastErrorAsync(connection.RemotePeer.Id,
                                                                   connection.RemotePeer.LastError);
            }
            this.connectedPeers.TryRemove(connection.RemotePeer.Id, out _);
        }
Exemple #3
0
        async Task RunNetworkPeerAsync(ConnectedPeer connectedPeer)
        {
            Debug.Assert(connectedPeer.PeerState.HasFlag(ConnectedPeer.State.Connected));

            try
            {
                await connectedPeer.VersionHandshakeAsync(this.chatClientConfiguration.UserAgentName, this.sessionNonce, this.cancellation.Token);

                connectedPeer.PeerState |= ConnectedPeer.State.VersionHandshake;

                this.ownAddresses.TryAdd(connectedPeer.SelfFromPeer.Id, connectedPeer.SelfFromPeer);

                // update also loaded (cached) instance and not only the repo
                connectedPeer.RemotePeer.PeerServices = connectedPeer.PeerVersionPayload.Services; // update services
                connectedPeer.RemotePeer.LastSeen     = DateTime.UtcNow;                           // update success
                connectedPeer.RemotePeer.LastError    = DateTime.MaxValue;                         // clear error
                await this.peerRepository.UpdatePeerAfterHandshakeAsync(connectedPeer.RemotePeer);

                this.messageRelayAddressReceiver.ReceiveMessageRelayRecordAsync(connectedPeer.RemotePeer.IPAddress,
                                                                                connectedPeer.RemotePeer.ProtocolPort,
                                                                                connectedPeer.RemotePeer.PeerServices, connectedPeer.PeerVersionPayload.UserAgent.Text);



                await connectedPeer.GetAddressesAsync(AddToAddressBookIfNotExistsAsync, this.cancellation.Token);

                connectedPeer.PeerState |= ConnectedPeer.State.AddrReceived;
            }
            catch (Exception e)
            {
                await HandleFailedConnectedPeerAsync(e, connectedPeer);
            }
        }
Exemple #4
0
        public async Task ConnectAndRunAsync(ConnectedPeer createdInstance)
        {
            var connectedInstance = await CreateConnectedPeerBlockingOrThrowAsync(createdInstance);

            if (connectedInstance != null)
            {
                this.logger.LogInformation(
                    $"Successfully created connected peer {createdInstance}, loading off to new thread.");
                await RunNetworkPeerAsync(createdInstance);
            }
        }
Exemple #5
0
        async Task <ConnectedPeer> CreateConnectedPeerBlockingOrThrowAsync(ConnectedPeer connectedPeer)
        {
            try
            {
                connectedPeer.PeerState |= ConnectedPeer.State.Connecting;
                await connectedPeer.ConnectAsync();

                this.ownAddresses.TryAdd(connectedPeer.SelfFromSocket.Id, connectedPeer.SelfFromSocket);
                connectedPeer.PeerState |= ConnectedPeer.State.Connected;

                return(connectedPeer);
            }
            catch (Exception e)
            {
                await HandleFailedConnectedPeerAsync(e, connectedPeer);

                return(null);
            }
        }
Exemple #6
0
        public async Task <ConnectedPeer> CreateNextPeerAsync()
        {
            IReadOnlyList <Peer> allPeers = await this.peerRepository.GetAllPeersAsync();

            if (allPeers.Count == 0)
            {
                this.logger.LogWarning("No peer addresses in the store - can't select a connection candidate!");
                return(null);
            }

            // peers, shortest success ago first
            var hotPeers = allPeers
                           .Where(x => !IsExcluded(
                                      x))                                         // e.g. if it's our own address, if we are already connected, or the last error is less than X minutes ago
                           .OrderByDescending(x => x.PeerServices.HasFlag(PeerServices.MessageRelay))
                           .ThenBy(x => DateTimeOffset.UtcNow - x.LastSeen)       // then, try the best candidates first
                           .ThenByDescending(x =>
                                             DateTimeOffset.UtcNow - x.LastError) // then, try the candidates where the last error is longest ago
                           .ToList();

            if (hotPeers.Count == 0)
            {
                this.logger.LogDebug(
                    "After applying the filtering rules, no connection candidates remain for selection.");
                return(null);
            }

            var candidate = hotPeers[0];

            this.logger.LogDebug(
                $"Selected connection candidate {candidate}, last seen {DateTimeOffset.UtcNow - candidate.LastSeen} ago, last error {DateTimeOffset.UtcNow - candidate.LastError} ago.");
            ConnectedPeer connectedPeer = new ConnectedPeer(candidate, this.loggerFactory);
            bool          addSuccess    = this.connectedPeers.TryAdd(connectedPeer.RemotePeer.Id, connectedPeer);

            Debug.Assert(addSuccess,
                         $"Bug: Peer {candidate} was already in the ConnectedPeers dictionary - that should not happen.");
            return(connectedPeer);
        }