public NetworkPeer(NetworkAddress peerAddress, Network network, NetworkPeerConnectionParameters parameters, IDateTimeProvider dateTimeProvider, ILoggerFactory loggerFactory) { this.loggerFactory = loggerFactory; this.logger = loggerFactory.CreateLogger(this.GetType().FullName, $"[{peerAddress.Endpoint}] "); this.logger.LogTrace("()"); this.dateTimeProvider = dateTimeProvider; parameters = parameters ?? new NetworkPeerConnectionParameters(); this.Inbound = false; this.Behaviors = new NetworkPeerBehaviorsCollection(this); this.MyVersion = parameters.CreateVersion(peerAddress.Endpoint, network, this.dateTimeProvider.GetTimeOffset()); this.Network = network; this.PeerAddress = peerAddress; this.LastSeen = peerAddress.Time.UtcDateTime; var socket = new Socket(AddressFamily.InterNetworkV6, SocketType.Stream, ProtocolType.Tcp); socket.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.IPv6Only, false); this.connection = new NetworkPeerConnection(this, socket, this.dateTimeProvider, this.loggerFactory); socket.ReceiveBufferSize = parameters.ReceiveBufferSize; socket.SendBufferSize = parameters.SendBufferSize; try { using (var completedEvent = new ManualResetEvent(false)) { using (var nodeSocketEventManager = NodeSocketEventManager.Create(completedEvent, peerAddress.Endpoint)) { this.logger.LogTrace("Connecting to '{0}'.", peerAddress.Endpoint); // If the socket connected straight away (synchronously) unblock all threads. if (!socket.ConnectAsync(nodeSocketEventManager.SocketEvent)) { completedEvent.Set(); } // Otherwise wait for the socket connection to complete OR if the operation got cancelled. WaitHandle.WaitAny(new WaitHandle[] { completedEvent, parameters.ConnectCancellation.WaitHandle }); parameters.ConnectCancellation.ThrowIfCancellationRequested(); if (nodeSocketEventManager.SocketEvent.SocketError != SocketError.Success) { throw new SocketException((int)nodeSocketEventManager.SocketEvent.SocketError); } var remoteEndpoint = (IPEndPoint)(socket.RemoteEndPoint ?? nodeSocketEventManager.SocketEvent.RemoteEndPoint); this.RemoteSocketAddress = remoteEndpoint.Address; this.RemoteSocketEndpoint = remoteEndpoint; this.RemoteSocketPort = remoteEndpoint.Port; this.State = NetworkPeerState.Connected; this.ConnectedAt = this.dateTimeProvider.GetUtcNow(); this.logger.LogTrace("Outbound connection to '{0}' established.", peerAddress.Endpoint); } } } catch (OperationCanceledException) { this.logger.LogTrace("Connection to '{0}' cancelled.", peerAddress.Endpoint); Utils.SafeCloseSocket(socket); this.State = NetworkPeerState.Offline; throw; } catch (Exception ex) { this.logger.LogTrace("Exception occurred: {0}", ex.ToString()); Utils.SafeCloseSocket(socket); this.DisconnectReason = new NetworkPeerDisconnectReason() { Reason = "Unexpected exception while connecting to socket", Exception = ex }; this.State = NetworkPeerState.Failed; throw; } this.InitDefaultBehaviors(parameters); this.connection.BeginListen(); this.logger.LogTrace("(-)"); }
public void BeginListen() { this.logger.LogTrace("()"); this.Disconnected = new ManualResetEvent(false); this.Cancel = new CancellationTokenSource(); new Thread(() => { this.logger.LogTrace("()"); SentMessage processing = null; Exception unhandledException = null; try { using (var completedEvent = new ManualResetEvent(false)) { using (var socketEventManager = NodeSocketEventManager.Create(completedEvent)) { socketEventManager.SocketEvent.SocketFlags = SocketFlags.None; foreach (SentMessage kv in this.Messages.GetConsumingEnumerable(this.Cancel.Token)) { processing = kv; Payload payload = kv.Payload; var message = new Message { Magic = this.Peer.Network.Magic, Payload = payload }; this.logger.LogTrace("Sending message: '{0}'", message); using (MemoryStream ms = new MemoryStream()) { message.ReadWrite(new BitcoinStream(ms, true) { ProtocolVersion = this.Peer.Version, TransactionOptions = this.Peer.SupportedTransactionOptions }); byte[] bytes = ms.ToArrayEfficient(); socketEventManager.SocketEvent.SetBuffer(bytes, 0, bytes.Length); this.Peer.Counter.AddWritten(bytes.Length); } completedEvent.Reset(); if (!this.Socket.SendAsync(socketEventManager.SocketEvent)) { Utils.SafeSet(completedEvent); } WaitHandle.WaitAny(new WaitHandle[] { completedEvent, this.Cancel.Token.WaitHandle }, -1); if (!this.Cancel.Token.IsCancellationRequested) { if (socketEventManager.SocketEvent.SocketError != SocketError.Success) { throw new SocketException((int)socketEventManager.SocketEvent.SocketError); } processing.Completion.SetResult(true); processing = null; } } } } } catch (OperationCanceledException) { this.logger.LogTrace("Sending cancelled."); } catch (Exception ex) { this.logger.LogTrace("Exception occurred: '{0}'", ex.ToString()); unhandledException = ex; } if (processing != null) { this.Messages.Add(processing); } foreach (SentMessage pending in this.Messages) { this.logger.LogTrace("Connection terminated before message '{0}' could be sent.", pending.Payload?.Command); pending.Completion.SetException(new OperationCanceledException("The peer has been disconnected")); } this.Messages = new BlockingCollection <SentMessage>(new ConcurrentQueue <SentMessage>()); this.logger.LogDebug("Terminating sending thread."); this.EndListen(unhandledException); this.logger.LogTrace("(-)"); }).Start(); new Thread(() => { this.logger.LogTrace("()"); this.ListenerThreadId = Thread.CurrentThread.ManagedThreadId; this.logger.LogTrace("Start listenting."); Exception unhandledException = null; byte[] buffer = this.Peer.ReuseBuffer ? new byte[1024 * 1024] : null; try { using (var stream = new NetworkStream(this.Socket, false)) { while (!this.Cancel.Token.IsCancellationRequested) { PerformanceCounter counter; Message message = Message.ReadNext(stream, this.Peer.Network, this.Peer.Version, this.Cancel.Token, buffer, out counter); this.logger.LogTrace("Receiving message: '{0}'", message); this.Peer.LastSeen = this.dateTimeProvider.GetUtcNow(); this.Peer.Counter.Add(counter); this.Peer.OnMessageReceived(new IncomingMessage() { Message = message, Socket = this.Socket, Length = counter.ReadBytes, NetworkPeer = this.Peer }); } } } catch (OperationCanceledException) { this.logger.LogTrace("Listening cancelled."); } catch (Exception ex) { this.logger.LogTrace("Exception occurred: {0}", ex); unhandledException = ex; } this.logger.LogDebug("Terminating listening thread."); this.EndListen(unhandledException); this.logger.LogTrace("(-)"); }).Start(); this.logger.LogTrace("(-)"); }