/// <summary> /// Sends a UDP message and expects a response. /// </summary> /// <param name="channelCreator">The channel creator will create a UDP connection.</param> /// <returns>The future task that was added in the constructor.</returns> public Task <Message.Message> SendUdpAsync(ChannelCreator channelCreator) { // so far, everything is sync -> invoke async / new thread var sendTask = ConnectionBean.Sender.SendUdpAsync(this, _tcsResponse, _message, channelCreator, IdleUdpSeconds, false); return(ExecuteAsync(sendTask)); }
/// <summary> /// Sends a TCP message and expects a response. /// </summary> /// <param name="channelCreator">The channel creator will create a TCP connection.</param> /// <param name="peerConnection"></param> /// <returns>The future task that was added in the constructor.</returns> public Task <Message.Message> SendTcpAsync(ChannelCreator channelCreator, PeerConnection peerConnection) { var sendTask = ConnectionBean.Sender.SendTcpAsync(this, _tcsResponse, _message, channelCreator, IdleTcpSeconds, ConnectionTimeoutTcpMillis, peerConnection); return(ExecuteAsync(sendTask)); }
/// <summary> /// Bootstraps to the given peer addresses. I.e., looking for near nodes. /// </summary> /// <param name="peerAddresses">The node to which bootstrap should be performed to.</param> /// <param name="routingBuilder">All relevant information for the routing process.</param> /// <param name="cc">The channel creator.</param> /// <returns>A task object that is set to complete if the route has been found.</returns> public Task<Pair<TcsRouting, TcsRouting>> Bootstrap(ICollection<PeerAddress> peerAddresses, RoutingBuilder routingBuilder, ChannelCreator cc) { // search close peers Logger.Debug("Bootstrap to {0}.", Convenient.ToString(peerAddresses)); var tcsDone = new TaskCompletionSource<Pair<TcsRouting, TcsRouting>>(); // first, we find close peers to us routingBuilder.IsBootstrap = true; var tcsRouting0 = Routing(peerAddresses, routingBuilder, Message.Message.MessageType.Request1, cc); // we need to know other peers as well // this is important if this peer is passive and only replies on requests from other peers tcsRouting0.Task.ContinueWith(taskRouting0 => { if (!taskRouting0.IsFaulted) { // setting this to null causes to search for a random number routingBuilder.LocationKey = null; var tcsRouting1 = Routing(peerAddresses, routingBuilder, Message.Message.MessageType.Request1, cc); tcsRouting1.Task.ContinueWith(taskRouting1 => { var pair = new Pair<TcsRouting, TcsRouting>(tcsRouting0, tcsRouting1); tcsDone.SetResult(pair); }); } else { tcsDone.SetException(taskRouting0.TryGetException()); } }); return tcsDone.Task; }
public void AddTcsDhtReleaseListener(ChannelCreator channelCreator) { Task.ContinueWith(tDht => { FutureRequests.Task.ContinueWith(tcsForkJoin => { channelCreator.ShutdownAsync(); }); }); }
/// <summary> /// If we don't have an open TCP connection, we first need a channel creator to open a channel. /// </summary> /// <param name="remotePeer">The remote peer to connect to.</param> /// <param name="cc">The channel creator where we can open a TCP connection.</param> /// <param name="heartBeatMillis"></param> public PeerConnection(PeerAddress remotePeer, ChannelCreator cc, int heartBeatMillis) { RemotePeer = remotePeer; ChannelCreator = cc; HeartBeatMillis = heartBeatMillis; _initiator = true; _oneConnection = new Semaphore(1, 1); _map = new Dictionary <TaskCompletionSource <ChannelCreator>, TaskCompletionSource <Message.Message> >(); _tcsClose = new TaskCompletionSource <object>(); }
public Task<Message.Message> SendAsync(PeerAddress remotePeer, ISendDirectBuilder sendDirectBuilder, ChannelCreator channelCreator) { var requestHandler = SendInternal(remotePeer, sendDirectBuilder); if (!sendDirectBuilder.IsForceUdp) { return requestHandler.SendTcpAsync(channelCreator); } return requestHandler.SendUdpAsync(channelCreator); }
private PeerConnection(Semaphore oneConnection, PeerAddress remotePeer, ChannelCreator cc, bool initiator, IDictionary <TaskCompletionSource <ChannelCreator>, TaskCompletionSource <Message.Message> > map, TaskCompletionSource <object> tcsClose, int heartBeatMillis, ITcpChannel channel) { _oneConnection = oneConnection; RemotePeer = remotePeer; ChannelCreator = cc; _initiator = initiator; _map = map; _tcsClose = tcsClose; HeartBeatMillis = heartBeatMillis; _channel = channel; }
/// <summary> /// Sends a message via TCP. /// </summary> /// <param name="handler">The handler to deal with the response message.</param> /// <param name="tcsResponse">The TCS for the response message. (FutureResponse equivalent.)</param> /// <param name="message">The message to send.</param> /// <param name="channelCreator">The channel creator for the TCP channel.</param> /// <param name="idleTcpSeconds">The idle time until message fail.</param> /// <param name="connectTimeoutMillis">The idle time for the connection setup.</param> /// <param name="peerConnection"></param> /// <returns></returns> public async Task SendTcpAsync(IInboundHandler handler, TaskCompletionSource <Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleTcpSeconds, int connectTimeoutMillis, PeerConnection peerConnection) { // no need to continue if already finished if (tcsResponse.Task.IsCompleted) { return; } RemovePeerIfFailed(tcsResponse, message); bool isFireAndForget = handler == null; // we need to set the neighbors if we use relays if (message.Sender.IsRelayed && message.Sender.PeerSocketAddresses.Count != 0) { message.SetPeerSocketAddresses(message.Sender.PeerSocketAddresses); } if (peerConnection != null && peerConnection.Channel != null && peerConnection.Channel.IsOpen) { var channel = SendTcpPeerConnection(peerConnection, handler, channelCreator, tcsResponse); await AfterConnectAsync(tcsResponse, message, channel, isFireAndForget); } else if (channelCreator != null) { var timeoutHandler = CreateTimeoutHandler(tcsResponse, idleTcpSeconds, handler == null); // check relay if (message.Recipient.IsRelayed) { // check if reverse connection is possible if (!message.Sender.IsRelayed) { await HandleRconAsync(handler, tcsResponse, message, channelCreator, connectTimeoutMillis, peerConnection, timeoutHandler); } else { await HandleRelayAsync(handler, tcsResponse, message, channelCreator, idleTcpSeconds, connectTimeoutMillis, peerConnection, timeoutHandler); } } // normal connection else { await ConnectAndSendAsync(handler, tcsResponse, channelCreator, connectTimeoutMillis, peerConnection, timeoutHandler, message); } } }
/// <summary> /// // TODO document /// </summary> /// <param name="handler"></param> /// <param name="tcsResponse"></param> /// <param name="message"></param> /// <param name="channelCreator"></param> /// <param name="idleTcpSeconds"></param> /// <param name="connectTimeoutMillis"></param> /// <param name="peerConnection"></param> /// <param name="timeoutHandler"></param> private async Task HandleRelayAsync(IInboundHandler handler, TaskCompletionSource <Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleTcpSeconds, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler) { var taskPingDone = PingFirst(message.Recipient.PeerSocketAddresses); await taskPingDone; if (!taskPingDone.IsFaulted) { var recipient = PeerSocketAddress.CreateSocketTcp(taskPingDone.Result); var channel = SendTcpCreateChannel(recipient, channelCreator, peerConnection, handler, timeoutHandler, connectTimeoutMillis); await AfterConnectAsync(tcsResponse, message, channel, handler == null); // TODO add this before AfterConnect? var taskResponse = tcsResponse.Task; await taskResponse; if (taskResponse.IsFaulted) { if (taskResponse.Result != null && taskResponse.Result.Type != Message.Message.MessageType.User1) { // "clearInactivePeerSocketAddress" var tmp = new List <PeerSocketAddress>(); foreach (var psa in message.Recipient.PeerSocketAddresses) { if (psa != null) { if (!psa.Equals(taskPingDone.Result)) { tmp.Add(psa); } } } message.SetPeerSocketAddresses(tmp); await SendTcpAsync(handler, tcsResponse, message, channelCreator, idleTcpSeconds, connectTimeoutMillis, peerConnection); } } } else { // .NET-specific: tcsResponse.SetException(new TaskFailedException("No relay could be contacted. <-> " + taskPingDone.Exception)); } }
/// <summary> /// Sends a message that indicates this peer is about to quit. This is an RPC. /// </summary> /// <param name="remotePeer">The remote peer to send this request.</param> /// <param name="shutdownBuilder">Used for the sign and force TCP flag. Set if the message should be signed.</param> /// <param name="channelCreator">The channel creator that creates connections.</param> /// <returns>The future response message.</returns> public Task<Message.Message> QuitAsync(PeerAddress remotePeer, ShutdownBuilder shutdownBuilder, ChannelCreator channelCreator) { var message = CreateRequestMessage(remotePeer, Rpc.Commands.Quit.GetNr(), Message.Message.MessageType.RequestFf1); if (shutdownBuilder.IsSign) { message.SetPublicKeyAndSign(shutdownBuilder.KeyPair); } var tcsResponse = new TaskCompletionSource<Message.Message>(message); var requestHandler = new RequestHandler(tcsResponse, PeerBean, ConnectionBean, shutdownBuilder); Logger.Debug("Send QUIT message {0}.", message); if (!shutdownBuilder.IsForceTcp) { return requestHandler.FireAndForgetUdpAsync(channelCreator); } return requestHandler.SendTcpAsync(channelCreator); }
public Task CloseAsync() { // cc is not null if we opened the connection if (ChannelCreator != null) { Logger.Debug("Close connection {0}. We were the initiator.", _channel); // maybe done on arrival? set close future in any case ChannelCreator.ShutdownAsync().ContinueWith(delegate { _tcsClose.SetResult(null); // complete }); } else { // cc is null if it is an incoming connection // we can close it here or it will be closed when the dispatcher is shut down Logger.Debug("Close connection {0}. We are not the initiator.", _channel); _channel.Close(); } return(_tcsClose.Task); }
/// <summary> /// Adds a channel creator to the set and also adds it to the shutdown listener. /// </summary> /// <param name="channelCreator"></param> private void AddToSet(ChannelCreator channelCreator) { channelCreator.ShutdownTask.ContinueWith(delegate { _readWriteLock.EnterReadLock(); try { if (_shutdown) { return; } //Console.WriteLine("Removing channel creator from set."); _channelCreators.Remove(channelCreator); } finally { _readWriteLock.ExitReadLock(); } }); _channelCreators.Add(channelCreator); }
public Task<Message.Message> SendAsync(PeerAddress remotePeer, BroadcastBuilder broadcastBuilder, ChannelCreator channelCreator, IConnectionConfiguration configuration) { var message = CreateRequestMessage(remotePeer, Rpc.Commands.Broadcast.GetNr(), Message.Message.MessageType.RequestFf1); message.SetIntValue(broadcastBuilder.HopCounter); message.SetKey(broadcastBuilder.MessageKey); if (broadcastBuilder.DataMap != null) { message.SetDataMap(new DataMap(broadcastBuilder.DataMap)); } var tcsResponse = new TaskCompletionSource<Message.Message>(message); var requestHandler = new RequestHandler(tcsResponse, PeerBean, ConnectionBean, configuration); if (!broadcastBuilder.IsUdp) { return requestHandler.SendTcpAsync(channelCreator); } else { return requestHandler.FireAndForgetUdpAsync(channelCreator); } }
private ITcpClientChannel SendTcpCreateChannel(IPEndPoint recipient, ChannelCreator channelCreator, PeerConnection peerConnection, IChannelHandler handler, TimeoutFactory timeoutHandler, int connectTimeoutMillis) { // create pipeline var handlers = new Dictionary <string, IChannelHandler>(); if (timeoutHandler != null) { handlers.Add("timeout0", timeoutHandler.CreateIdleStateHandlerTomP2P()); handlers.Add("timeout1", timeoutHandler.CreateTimeHandler()); } handlers.Add("decoder", new TomP2PCumulationTcp(ChannelClientConfiguration.SignatureFactory)); handlers.Add("encoder", new TomP2POutbound(false, ChannelClientConfiguration.SignatureFactory)); if (peerConnection != null) { // we expect responses on this connection handlers.Add("dispatcher", _dispatcher); } if (timeoutHandler != null) { handlers.Add("handler", handler); } HeartBeat heartBeat = null; if (peerConnection != null) { heartBeat = new HeartBeat(peerConnection.HeartBeatMillis, PingBuilderFactory); handlers.Add("heartbeat", heartBeat); } var channel = channelCreator.CreateTcp(recipient, connectTimeoutMillis, handlers); if (peerConnection != null && channel != null) { peerConnection.SetChannel(channel); heartBeat.SetPeerConnection(peerConnection); } return(channel); }
/// <summary> /// This method initiates the reverse connection setup. /// It creates a new message and sends it via relay to the unreachable peer /// which then connects to this peer again. After the connect message from the /// unreachable peer, this peer will send the original message and its content /// directly. /// </summary> /// <param name="handler"></param> /// <param name="tcsResponse"></param> /// <param name="message"></param> /// <param name="channelCreator"></param> /// <param name="connectTimeoutMillis"></param> /// <param name="peerConnection"></param> /// <param name="timeoutHandler"></param> private async Task HandleRconAsync(IInboundHandler handler, TaskCompletionSource <Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler) { message.SetKeepAlive(true); Logger.Debug("Initiate reverse connection setup to peer with address {0}.", message.Recipient); var rconMessage = CreateRconMessage(message); // TODO works? // cache the original message until the connection is established _cachedRequests.AddOrUpdate(message.MessageId, tcsResponse, (i, source) => tcsResponse); // wait for response (whether the reverse connection setup was successful) var tcsRconResponse = new TaskCompletionSource <Message.Message>(rconMessage); // .NET-specific: specify and use a RconInboundHandler class var rconInboundHandler = new RconInboundHandler(tcsRconResponse, tcsResponse); // send reverse connection request instead of normal message await SendTcpAsync(rconInboundHandler, tcsRconResponse, rconMessage, channelCreator, connectTimeoutMillis, connectTimeoutMillis, peerConnection); }
/// <summary> /// .NET-specific: Used for DistributedRouting only. /// </summary> internal TaskCompletionSource<Message.Message> CloseNeighborsTcs(PeerAddress remotePeer, SearchValues searchValues, Message.Message.MessageType type, ChannelCreator channelCreator, IConnectionConfiguration configuration) { var message = CreateRequestMessage(remotePeer, Rpc.Commands.Neighbor.GetNr(), type); if (!message.IsRequest()) { throw new ArgumentException("The type must be a request."); } message.SetKey(searchValues.LocationKey); message.SetKey(searchValues.DomainKey ?? Number160.Zero); if (searchValues.From != null && searchValues.To != null) { ICollection<Number640> collection = new List<Number640>(); collection.Add(searchValues.From); collection.Add(searchValues.To); var keyCollection = new KeyCollection(collection); message.SetKeyCollection(keyCollection); } else { if (searchValues.ContentKey != null) { message.SetKey(searchValues.ContentKey); } if (searchValues.KeyBloomFilter != null) { message.SetBloomFilter(searchValues.KeyBloomFilter); } if (searchValues.ContentBloomFilter != null) { message.SetBloomFilter(searchValues.ContentBloomFilter); } } return Send(message, configuration, channelCreator); }
private ITcpClientChannel SendTcpPeerConnection(PeerConnection peerConnection, IChannelHandler handler, ChannelCreator channelCreator, TaskCompletionSource <Message.Message> tcsResponse) { // if the channel gets closed, the future should get notified var channel = peerConnection.Channel; // channel creator can be null if we don't need to create any channels if (channelCreator != null) { // TODO this doesn't do anything yet channelCreator.SetupCloseListener(channel, tcsResponse); } // we need to replace the handler if this comes from the peer that created a peer connection, // otherwise we need to add a handler AddOrReplace(channel.Pipeline, "dispatcher", "handler", handler); // TODO uncommented Java stuff needed? return(channel as ITcpClientChannel); // TODO this will fail if its a server channel!!! }
private TaskCompletionSource<Message.Message> Send(Message.Message message, IConnectionConfiguration configuration, ChannelCreator channelCreator) { var tcsResponse = new TaskCompletionSource<Message.Message>(message); tcsResponse.Task.ContinueWith(taskResponse => { if (!taskResponse.IsFaulted) { var response = taskResponse.Result; if (response != null) { var neighborSet = response.NeighborsSet(0); if (neighborSet != null) { foreach (var neighbor in neighborSet.Neighbors) { lock (PeerBean.PeerStatusListeners) { foreach (var listener in PeerBean.PeerStatusListeners) { listener.PeerFound(neighbor, response.Sender, null); } } } } } } }); var requestHandler = new RequestHandler(tcsResponse, PeerBean, ConnectionBean, configuration); if (!configuration.IsForceTcp) { requestHandler.SendUdpAsync(channelCreator); } else { requestHandler.SendTcpAsync(channelCreator); } // .NET-specific: Return TCS instead of Task. It's actually the same TCS that is provided with // the RequestHandler c'tor return tcsResponse; }
/// <summary> /// Requests close neighbors from the remote peer. The remote peer may indicate if the /// data is present on that peer. This is an RPC. /// </summary> /// <param name="remotePeer">The remote peer to send this request to.</param> /// <param name="searchValues">The values to search for in the storage.</param> /// <param name="type">The type of the neighbor request: /// - Request1 for Neighbors means check for Put (no digest) for tracker and storage. /// - Request2 for Neighbors means check for Get (with digest) for storage. /// - Request3 for Neighbors means check for Get (with digest) for tracker. /// - Request4 for Neighbors means check for Put (with digest) for task.</param> /// <param name="channelCreator">The channel creator that creates connections.</param> /// <param name="configuration">The client-side connection configuration.</param> /// <returns>The future response message.</returns> public Task<Message.Message> CloseNeighborsAsync(PeerAddress remotePeer, SearchValues searchValues, Message.Message.MessageType type, ChannelCreator channelCreator, IConnectionConfiguration configuration) { var tcsResponse = CloseNeighborsTcs(remotePeer, searchValues, type, channelCreator, configuration); return tcsResponse.Task; }
/// <summary> /// Sends a message via UDP. /// </summary> /// <param name="handler">The handler to deal with the response message.</param> /// <param name="tcsResponse">The TCS for the response message. (FutureResponse equivalent.)</param> /// <param name="message">The message to send.</param> /// <param name="channelCreator">The channel creator for the UDP channel.</param> /// <param name="idleUdpSeconds">The idle time of a message until fail.</param> /// <param name="broadcast">True, if the message is to be sent via layer 2 broadcast.</param> /// <returns>The response message or null, if it is fire-and-forget or a failure occurred.</returns> public async Task SendUdpAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleUdpSeconds, bool broadcast) { // no need to continue if already finished if (tcsResponse.Task.IsCompleted) { return; } RemovePeerIfFailed(tcsResponse, message); bool isFireAndForget = handler == null; // relay options if (message.Sender.IsRelayed) { message.SetPeerSocketAddresses(message.Sender.PeerSocketAddresses); IList<PeerSocketAddress> relayAddresses = new List<PeerSocketAddress>(message.Recipient.PeerSocketAddresses); Logger.Debug("Send neighbor request to random relay peer {0}.", relayAddresses); if (relayAddresses.Count > 0) { var relayAddress = relayAddresses[_random.NextInt(relayAddresses.Count)]; message.SetRecipientRelay(message.Recipient .ChangePeerSocketAddress(relayAddress) .ChangeIsRelayed(true)); } else { const string msg = "Peer is relayed, but no relay is given."; Logger.Error(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } } // check for invalid UDP connection to unreachable peers if (message.Recipient.IsRelayed && message.Command != Rpc.Rpc.Commands.Neighbor.GetNr() && message.Command != Rpc.Rpc.Commands.Ping.GetNr()) { string msg = String.Format( "Tried to send a UDP message to unreachable peers. Only TCP messages can be sent to unreachable peers: {0}.", message); Logger.Warn(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } // pipeline handler setup TimeoutFactory timeoutFactory = CreateTimeoutHandler(tcsResponse, idleUdpSeconds, isFireAndForget); var handlers = new Dictionary<string, IChannelHandler>(); if (!isFireAndForget) { handlers.Add("timeout0", timeoutFactory.CreateIdleStateHandlerTomP2P()); handlers.Add("timeout1", timeoutFactory.CreateTimeHandler()); } handlers.Add("decoder", new TomP2PSinglePacketUdp(ChannelClientConfiguration.SignatureFactory)); handlers.Add("encoder", new TomP2POutbound(false, ChannelClientConfiguration.SignatureFactory)); if (!isFireAndForget) { handlers.Add("handler", handler); } // create UDP channel MyUdpClient udpClient = null; try { udpClient = channelCreator.CreateUdp(broadcast, handlers); } catch (Exception ex) { string msg = "Channel creation failed. " + ex; Logger.Debug(msg); tcsResponse.SetException(ex); // may have been closed by the other side // or it may have been canceled from this side } // "afterConnect" // check if channel could be created (due to shutdown) if (udpClient == null) { const string msg = "Could not create a UDP socket. (Due to shutdown.)"; Logger.Warn(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } Logger.Debug("About to connect to {0} with channel {1}, ff = {2}.", message.Recipient, udpClient, isFireAndForget); // send request message // processes client-side outbound pipeline // (await for possible exception re-throw, does not block) await udpClient.SendMessageAsync(message); // if not fire-and-forget, receive response if (isFireAndForget) { Logger.Debug("Fire and forget message {0} sent. Close channel {1} now.", message, udpClient); tcsResponse.SetResult(null); // set FF result } else { // receive response message // processes client-side inbound pipeline await udpClient.ReceiveMessageAsync(); } udpClient.Close(); }
/// <summary> /// Sends a TCP message and expects a response. /// </summary> /// <param name="channelCreator">The channel creator will create a TCP connection.</param> /// <param name="peerConnection"></param> /// <returns>The future task that was added in the constructor.</returns> public Task<Message.Message> SendTcpAsync(ChannelCreator channelCreator, PeerConnection peerConnection) { var sendTask = ConnectionBean.Sender.SendTcpAsync(this, _tcsResponse, _message, channelCreator, IdleTcpSeconds, ConnectionTimeoutTcpMillis, peerConnection); return ExecuteAsync(sendTask); }
/// <summary> /// Sends a message via UDP. /// </summary> /// <param name="handler">The handler to deal with the response message.</param> /// <param name="tcsResponse">The TCS for the response message. (FutureResponse equivalent.)</param> /// <param name="message">The message to send.</param> /// <param name="channelCreator">The channel creator for the UDP channel.</param> /// <param name="idleUdpSeconds">The idle time of a message until fail.</param> /// <param name="broadcast">True, if the message is to be sent via layer 2 broadcast.</param> /// <returns>The response message or null, if it is fire-and-forget or a failure occurred.</returns> public async Task SendUdpAsync(IInboundHandler handler, TaskCompletionSource <Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleUdpSeconds, bool broadcast) { // no need to continue if already finished if (tcsResponse.Task.IsCompleted) { return; } RemovePeerIfFailed(tcsResponse, message); bool isFireAndForget = handler == null; // relay options if (message.Sender.IsRelayed) { message.SetPeerSocketAddresses(message.Sender.PeerSocketAddresses); IList <PeerSocketAddress> relayAddresses = new List <PeerSocketAddress>(message.Recipient.PeerSocketAddresses); Logger.Debug("Send neighbor request to random relay peer {0}.", relayAddresses); if (relayAddresses.Count > 0) { var relayAddress = relayAddresses[_random.NextInt(relayAddresses.Count)]; message.SetRecipientRelay(message.Recipient .ChangePeerSocketAddress(relayAddress) .ChangeIsRelayed(true)); } else { const string msg = "Peer is relayed, but no relay is given."; Logger.Error(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } } // check for invalid UDP connection to unreachable peers if (message.Recipient.IsRelayed && message.Command != Rpc.Rpc.Commands.Neighbor.GetNr() && message.Command != Rpc.Rpc.Commands.Ping.GetNr()) { string msg = String.Format( "Tried to send a UDP message to unreachable peers. Only TCP messages can be sent to unreachable peers: {0}.", message); Logger.Warn(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } // pipeline handler setup TimeoutFactory timeoutFactory = CreateTimeoutHandler(tcsResponse, idleUdpSeconds, isFireAndForget); var handlers = new Dictionary <string, IChannelHandler>(); if (!isFireAndForget) { handlers.Add("timeout0", timeoutFactory.CreateIdleStateHandlerTomP2P()); handlers.Add("timeout1", timeoutFactory.CreateTimeHandler()); } handlers.Add("decoder", new TomP2PSinglePacketUdp(ChannelClientConfiguration.SignatureFactory)); handlers.Add("encoder", new TomP2POutbound(false, ChannelClientConfiguration.SignatureFactory)); if (!isFireAndForget) { handlers.Add("handler", handler); } // create UDP channel MyUdpClient udpClient = null; try { udpClient = channelCreator.CreateUdp(broadcast, handlers); } catch (Exception ex) { string msg = "Channel creation failed. " + ex; Logger.Debug(msg); tcsResponse.SetException(ex); // may have been closed by the other side // or it may have been canceled from this side } // "afterConnect" // check if channel could be created (due to shutdown) if (udpClient == null) { const string msg = "Could not create a UDP socket. (Due to shutdown.)"; Logger.Warn(msg); tcsResponse.SetException(new TaskFailedException(msg)); return; } Logger.Debug("About to connect to {0} with channel {1}, ff = {2}.", message.Recipient, udpClient, isFireAndForget); // send request message // processes client-side outbound pipeline // (await for possible exception re-throw, does not block) await udpClient.SendMessageAsync(message); // if not fire-and-forget, receive response if (isFireAndForget) { Logger.Debug("Fire and forget message {0} sent. Close channel {1} now.", message, udpClient); tcsResponse.SetResult(null); // set FF result } else { // receive response message // processes client-side inbound pipeline await udpClient.ReceiveMessageAsync(); } udpClient.Close(); }
/// <summary> /// Creates a channel creator for permanent TCP connections. /// </summary> /// <param name="permitsPermanentTcp">The number of long-lived TCP connections.</param> /// <returns>The future channel creator.</returns> public Task <ChannelCreator> CreatePermanentAsync(int permitsPermanentTcp) { if (permitsPermanentTcp > _maxPermitsPermanentTcp) { throw new ArgumentException(String.Format("Cannot acquire more permantent TCP connections ({0}) than maximally allowed ({1}).", permitsPermanentTcp, _maxPermitsPermanentTcp)); } var tcsChannelCreator = new TaskCompletionSource <ChannelCreator>(); _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); return(tcsChannelCreator.Task); } var tcsChannelCreationDone = new TaskCompletionSource <object>(); tcsChannelCreationDone.Task.ContinueWith(delegate { // release the permits in all cases // otherwise, we may see inconsistencies _semaphorePermanentTcp.Release(permitsPermanentTcp); }); // instead of Executor.execute(new WaitReservationPermanent()) _singleThreadTaskFactory.StartNew(delegate { // Creates a reservation that returns a channel creator in a // task, once we have the semaphore. // Tries to reserve a channel creator. If too many channels are already // created, wait until channels are closed. ChannelCreator channelCreator; _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); } try { _semaphorePermanentTcp.Acquire(permitsPermanentTcp); } catch (Exception ex) { tcsChannelCreator.SetException(ex); return; } channelCreator = new ChannelCreator(tcsChannelCreationDone, 0, permitsPermanentTcp, _channelClientConfiguration); AddToSet(channelCreator); } finally { _readWriteLock.ExitReadLock(); } tcsChannelCreator.SetResult(channelCreator); }); return(tcsChannelCreator.Task); } finally { _readWriteLock.ExitReadLock(); } }
private void RoutingRec(RoutingBuilder routingBuilder, RoutingMechanism routingMechanism, Message.Message.MessageType type, ChannelCreator channelCreator) { var randomSearch = routingBuilder.LocationKey == null; var active = 0; for (var i = 0; i < routingMechanism.Parallel; i++) { if (routingMechanism.GetTcsResponse(i) == null && !routingMechanism.IsStopCreatingNewFutures) { PeerAddress next; if (randomSearch) { next = routingMechanism.PollRandomInQueueToAsk(_rnd); } else { next = routingMechanism.PollFirstInQueueToAsk(); } if (next != null) { routingMechanism.AddToAlreadyAsked(next); active++; // If we search for a random peer, then the peer should // return the address farest away. var locationKey2 = randomSearch ? next.PeerId.Xor(Number160.MaxValue) : routingBuilder.LocationKey; routingBuilder.LocationKey = locationKey2; // routing is per default UDP, don't show warning if the other TCP/UDP is used // TODO find .NET-specific way to show sanity check warning routingMechanism.SetTcsResponse(i, _neighbors.CloseNeighborsTcs(next, routingBuilder.SearchValues(), type, channelCreator, routingBuilder)); Logger.Debug("Get close neighbours: {0} on {1}.", next, i); } } else if (routingMechanism.GetTcsResponse(i) != null) { Logger.Debug("Activity on {0}.", i); active++; } } if (active == 0) { Logger.Debug("No activity, closing."); routingMechanism.SetNeighbors(routingBuilder); routingMechanism.Cancel(); return; } // .NET-specific: // TODO move to TcsForkJoin as separate c'tor? var extractedTasks = new Task<Message.Message>[routingMechanism.TcsResponses.Length]; for (int i = 0; i < routingMechanism.TcsResponses.Length; i++) { extractedTasks[i] = routingMechanism.GetTcsResponse(i) != null ? routingMechanism.GetTcsResponse(i).Task : null; } var volatileArray = new VolatileReferenceArray<Task<Message.Message>>(extractedTasks); bool last = active == 1; var tcsForkJoin = new TcsForkJoin<Task<Message.Message>>(1, false, volatileArray); tcsForkJoin.Task.ContinueWith(tfj => { bool finished; if (!tfj.IsFaulted) { var lastResponse = tcsForkJoin.Last.Result; var remotePeer = lastResponse.Sender; routingMechanism.AddPotentialHits(remotePeer); var newNeighbors = lastResponse.NeighborsSet(0).Neighbors; var resultSize = lastResponse.IntAt(0); var keyDigest = lastResponse.Key(0); var contentDigest = lastResponse.Key(1); var digestBean = new DigestInfo(keyDigest, contentDigest, resultSize); Logger.Debug("Peer ({0}) {1} reported {2} in message {3}.", (digestBean.Size > 0 ? "direct" : "none"), remotePeer, newNeighbors.Count, lastResponse); finished = routingMechanism.EvaluateSuccess(remotePeer, digestBean, newNeighbors, last, routingBuilder.LocationKey); Logger.Debug("Routing finished {0} / {1}.", finished, routingMechanism.IsStopCreatingNewFutures); } else { // if it failed but the failed is the closest one, it is good to try again, // since the peer might just be busy Logger.Debug("Routing error {0}.", tfj.Exception); finished = routingMechanism.EvaluateFailed(); routingMechanism.IsStopCreatingNewFutures = finished; } if (finished) { Logger.Debug("Routing finished. Direct hits: {0}. Potential hits: {1}.", routingMechanism.DirectHits.Count, routingMechanism.PotentialHits.Count); routingMechanism.SetNeighbors(routingBuilder); routingMechanism.Cancel(); // stop all operations, as we are finished, no need to go further } else { RoutingRec(routingBuilder, routingMechanism, type, channelCreator); } }); }
public TcsRouting Quit(RoutingBuilder routingBuilder, ChannelCreator cc) { ICollection<PeerAddress> startPeers = _peerBean.PeerMap.ClosePeers(routingBuilder.LocationKey, routingBuilder.Parallel * 2); return Routing(startPeers, routingBuilder, Message.Message.MessageType.Request4, cc); }
/// <summary> /// Broadscasts a UDP message (layer 2) and expects a response. /// </summary> /// <param name="channelCreator">The channel creator will create a UDP connection.</param> /// <returns>The future task that was added in the constructor.</returns> public Task<Message.Message> SendBroadcastUdpAsync(ChannelCreator channelCreator) { var sendTask = ConnectionBean.Sender.SendUdpAsync(this, _tcsResponse, _message, channelCreator, IdleUdpSeconds, true); return ExecuteAsync(sendTask); }
/// <summary> /// Sends a UDP message and expects a response. /// </summary> /// <param name="channelCreator">The channel creator will create a UDP connection.</param> /// <returns>The future task that was added in the constructor.</returns> public Task<Message.Message> SendUdpAsync(ChannelCreator channelCreator) { // so far, everything is sync -> invoke async / new thread var sendTask = ConnectionBean.Sender.SendUdpAsync(this, _tcsResponse, _message, channelCreator, IdleUdpSeconds, false); return ExecuteAsync(sendTask); }
private async Task ConnectAndSendAsync(IInboundHandler handler, TaskCompletionSource <Message.Message> tcsResponse, ChannelCreator channelCreator, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler, Message.Message message) { var recipient = message.Recipient.CreateSocketTcp(); var channel = SendTcpCreateChannel(recipient, channelCreator, peerConnection, handler, timeoutHandler, connectTimeoutMillis); await AfterConnectAsync(tcsResponse, message, channel, handler == null); }
/// <summary> /// Creates a channel creator for permanent TCP connections. /// </summary> /// <param name="permitsPermanentTcp">The number of long-lived TCP connections.</param> /// <returns>The future channel creator.</returns> public Task<ChannelCreator> CreatePermanentAsync(int permitsPermanentTcp) { if (permitsPermanentTcp > _maxPermitsPermanentTcp) { throw new ArgumentException(String.Format("Cannot acquire more permantent TCP connections ({0}) than maximally allowed ({1}).", permitsPermanentTcp, _maxPermitsPermanentTcp)); } var tcsChannelCreator = new TaskCompletionSource<ChannelCreator>(); _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); return tcsChannelCreator.Task; } var tcsChannelCreationDone = new TaskCompletionSource<object>(); tcsChannelCreationDone.Task.ContinueWith(delegate { // release the permits in all cases // otherwise, we may see inconsistencies _semaphorePermanentTcp.Release(permitsPermanentTcp); }); // instead of Executor.execute(new WaitReservationPermanent()) _singleThreadTaskFactory.StartNew(delegate { // Creates a reservation that returns a channel creator in a // task, once we have the semaphore. // Tries to reserve a channel creator. If too many channels are already // created, wait until channels are closed. ChannelCreator channelCreator; _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); } try { _semaphorePermanentTcp.Acquire(permitsPermanentTcp); } catch (Exception ex) { tcsChannelCreator.SetException(ex); return; } channelCreator = new ChannelCreator(tcsChannelCreationDone, 0, permitsPermanentTcp, _channelClientConfiguration); AddToSet(channelCreator); } finally { _readWriteLock.ExitReadLock(); } tcsChannelCreator.SetResult(channelCreator); }); return tcsChannelCreator.Task; } finally { _readWriteLock.ExitReadLock(); } }
/// <summary> /// Sends a UDP message and doesn't expect a response. /// </summary> /// <param name="channelCreator">The channel creator will create a UDP connection.</param> /// <returns>The future task that was added in the constructor.</returns> public Task <Message.Message> FireAndForgetUdpAsync(ChannelCreator channelCreator) { var sendTask = ConnectionBean.Sender.SendUdpAsync(null, _tcsResponse, _message, channelCreator, 0, false); return(ExecuteAsync(sendTask)); }
/// <summary> /// Looks for a route to the given peer address. /// </summary> /// <param name="peerAddresses">Nodes that should be asked first for a route.</param> /// <param name="routingBuilder"></param> /// <param name="type"></param> /// <param name="cc"></param> /// <returns>A task object that is set to complete if the route has been found.</returns> private TcsRouting Routing(ICollection<PeerAddress> peerAddresses, RoutingBuilder routingBuilder, Message.Message.MessageType type, ChannelCreator cc) { try { if (peerAddresses == null) { throw new ArgumentException("Some nodes/addresses need to be specified."); } bool randomSearch = routingBuilder.LocationKey == null; IComparer<PeerAddress> comparer; if (randomSearch) { comparer = _peerBean.PeerMap.CreateComparer(); } else { comparer = PeerMap.CreateComparer(routingBuilder.LocationKey); } var queueToAsk = new SortedSet<PeerAddress>(comparer); var alreadyAsked = new SortedSet<PeerAddress>(comparer); // As presented by Kazuyuki Shudo at AIMS 2009, it is better to ask random // peers with the data than ask peers that are ordered by distance. // -> this balances load var directHits = new SortedDictionary<PeerAddress, DigestInfo>(comparer); var potentialHits = new SortedSet<PeerAddress>(comparer); // fill initially queueToAsk.AddAll(peerAddresses); alreadyAsked.Add(_peerBean.ServerPeerAddress); potentialHits.Add(_peerBean.ServerPeerAddress); // domain key can be null if we bootstrap if (type == Message.Message.MessageType.Request2 && routingBuilder.DomainKey != null && !randomSearch && _peerBean.DigestStorage != null) { Number640 from; Number640 to; if (routingBuilder.From != null && routingBuilder.To != null) { from = routingBuilder.From; to = routingBuilder.To; } else if (routingBuilder.ContentKey == null) { from = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, Number160.Zero, Number160.Zero); to = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, Number160.MaxValue, Number160.MaxValue); } else { from = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, routingBuilder.ContentKey, Number160.Zero); to = new Number640(routingBuilder.LocationKey, routingBuilder.DomainKey, routingBuilder.ContentKey, Number160.MaxValue); } var digestBean = _peerBean.DigestStorage.Digest(from, to, -1, true); if (digestBean.Size > 0) { directHits.Add(_peerBean.ServerPeerAddress, digestBean); } } else if (type == Message.Message.MessageType.Request3 && !randomSearch && _peerBean.DigestTracker != null) { var digestInfo = _peerBean.DigestTracker.Digest(routingBuilder.LocationKey, routingBuilder.DomainKey, routingBuilder.ContentKey); // we always put ourselfs to the tracker list, so we need to check // if we know also other peers on our trackers if (digestInfo.Size > 0) { directHits.Add(_peerBean.ServerPeerAddress, digestInfo); } } var tcsRouting = new TcsRouting(); if (peerAddresses.Count == 0) { tcsRouting.SetNeighbors(directHits, potentialHits, alreadyAsked, routingBuilder.IsBootstrap, false); } else { // If a peer bootstraps to itself, then the size of peer addresses is 1 // and it contains itself. Check for that because we need to know if we // are routing, bootstrapping and bootstrapping to ourselfs, to return // the correct status for the task. var isRoutingOnlyToSelf = peerAddresses.Count == 1 && peerAddresses.First().Equals(_peerBean.ServerPeerAddress); var routingMechanism = routingBuilder.CreateRoutingMechanism(tcsRouting); routingMechanism.SetQueueToAsk(queueToAsk); routingMechanism.SetPotentialHits(potentialHits); routingMechanism.SetDirectHits(directHits); routingMechanism.SetAlreadyAsked(alreadyAsked); routingBuilder.SetIsRoutingOnlyToSelf(isRoutingOnlyToSelf); RoutingRec(routingBuilder, routingMechanism, type, cc); } return tcsRouting; } catch (Exception ex) { Logger.Error("An exception occurred during routing.", ex); throw; } }
/// <summary> /// Sends a UDP message and doesn't expect a response. /// </summary> /// <param name="channelCreator">The channel creator will create a UDP connection.</param> /// <returns>The future task that was added in the constructor.</returns> public Task<Message.Message> FireAndForgetUdpAsync(ChannelCreator channelCreator) { var sendTask = ConnectionBean.Sender.SendUdpAsync(null, _tcsResponse, _message, channelCreator, 0, false); return ExecuteAsync(sendTask); }
/// <summary> /// Sends a message via TCP. /// </summary> /// <param name="handler">The handler to deal with the response message.</param> /// <param name="tcsResponse">The TCS for the response message. (FutureResponse equivalent.)</param> /// <param name="message">The message to send.</param> /// <param name="channelCreator">The channel creator for the TCP channel.</param> /// <param name="idleTcpSeconds">The idle time until message fail.</param> /// <param name="connectTimeoutMillis">The idle time for the connection setup.</param> /// <param name="peerConnection"></param> /// <returns></returns> public async Task SendTcpAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleTcpSeconds, int connectTimeoutMillis, PeerConnection peerConnection) { // no need to continue if already finished if (tcsResponse.Task.IsCompleted) { return; } RemovePeerIfFailed(tcsResponse, message); bool isFireAndForget = handler == null; // we need to set the neighbors if we use relays if (message.Sender.IsRelayed && message.Sender.PeerSocketAddresses.Count != 0) { message.SetPeerSocketAddresses(message.Sender.PeerSocketAddresses); } if (peerConnection != null && peerConnection.Channel != null && peerConnection.Channel.IsOpen) { var channel = SendTcpPeerConnection(peerConnection, handler, channelCreator, tcsResponse); await AfterConnectAsync(tcsResponse, message, channel, isFireAndForget); } else if (channelCreator != null) { var timeoutHandler = CreateTimeoutHandler(tcsResponse, idleTcpSeconds, handler == null); // check relay if (message.Recipient.IsRelayed) { // check if reverse connection is possible if (!message.Sender.IsRelayed) { await HandleRconAsync(handler, tcsResponse, message, channelCreator, connectTimeoutMillis, peerConnection, timeoutHandler); } else { await HandleRelayAsync(handler, tcsResponse, message, channelCreator, idleTcpSeconds, connectTimeoutMillis, peerConnection, timeoutHandler); } } // normal connection else { await ConnectAndSendAsync(handler, tcsResponse, channelCreator, connectTimeoutMillis, peerConnection, timeoutHandler, message); } } }
/// <summary> /// Creates a channel creator for short-lived connections. /// Always call <see cref="ChannelCreator.ShutdownAsync"/> to release all resources. /// (This needs to be done in any case, whether it succeeds or fails.) /// </summary> /// <param name="permitsUdp">The number of short-lived UDP connections.</param> /// <param name="permitsTcp">The number of short-lived TCP connections.</param> /// <returns>The future channel creator.</returns> public Task <ChannelCreator> CreateAsync(int permitsUdp, int permitsTcp) { if (permitsUdp > _maxPermitsUdp) { throw new ArgumentException(String.Format("Cannot acquire more UDP connections ({0}) than maximally allowed ({1}).", permitsUdp, _maxPermitsUdp)); } if (permitsTcp > _maxPermitsTcp) { throw new ArgumentException(String.Format("Cannot acquire more TCP connections ({0}) than maximally allowed ({1}).", permitsTcp, _maxPermitsTcp)); } var tcsChannelCreator = new TaskCompletionSource <ChannelCreator>(); _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); return(tcsChannelCreator.Task); } var tcsChannelCreationDone = new TaskCompletionSource <object>(); tcsChannelCreationDone.Task.ContinueWith(delegate { // release the permits in all cases // otherwise, we may see inconsistencies //Console.WriteLine("Reservation ({0}): A CC shut down. Releasing {1} UDP, {2} TCP permits.", RuntimeHelpers.GetHashCode(this), permitsUdp, permitsTcp); _semaphoreUdp.Release2(permitsUdp); _semaphoreTcp.Release2(permitsTcp); }); // instead of Executor.execute(new WaitReservation()) _singleThreadTaskFactory.StartNew(delegate { //Console.WriteLine("Reservation ({0}): Executing async reservation...", RuntimeHelpers.GetHashCode(this)); // Creates a reservation that returns a channel creator in a // task, once we have the semaphore. // Tries to reserve a channel creator. If too many channels are already // created, wait until channels are closed. ChannelCreator channelCreator; _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); return; } try { //Console.Write("[{0}] Reservation ({1}): Acquiring {2} UDP permits.", Thread.CurrentThread.ManagedThreadId, RuntimeHelpers.GetHashCode(this), permitsUdp); _semaphoreUdp.Acquire(permitsUdp); //Console.Write("({0}) --> granted\n", RuntimeHelpers.GetHashCode(this)); } catch (Exception ex) { tcsChannelCreator.SetException(ex); return; } try { _semaphoreTcp.Acquire(permitsTcp); } catch (Exception ex) { _semaphoreUdp.Release(permitsUdp); tcsChannelCreator.SetException(ex); return; } channelCreator = new ChannelCreator(tcsChannelCreationDone, permitsUdp, permitsTcp, _channelClientConfiguration); AddToSet(channelCreator); } finally { _readWriteLock.ExitReadLock(); } tcsChannelCreator.SetResult(channelCreator); }); return(tcsChannelCreator.Task); } finally { _readWriteLock.ExitReadLock(); } }
/// <summary> /// Looks for a route to the location key given in the routing builder. /// </summary> /// <param name="routingBuilder">All relevant information for the routing process.</param> /// <param name="type">The type of the routing. (4 types)</param> /// <param name="cc">The channel creator.</param> /// <returns>A task object that is set to complete if the route has been found.</returns> public TcsRouting Route(RoutingBuilder routingBuilder, Message.Message.MessageType type, ChannelCreator cc) { // for bad distribution, use large #noNewInformation ICollection<PeerAddress> startPeers = _peerBean.PeerMap.ClosePeers(routingBuilder.LocationKey, routingBuilder.Parallel * 2); return Routing(startPeers, routingBuilder, type, cc); }
/// <summary> /// This method initiates the reverse connection setup. /// It creates a new message and sends it via relay to the unreachable peer /// which then connects to this peer again. After the connect message from the /// unreachable peer, this peer will send the original message and its content /// directly. /// </summary> /// <param name="handler"></param> /// <param name="tcsResponse"></param> /// <param name="message"></param> /// <param name="channelCreator"></param> /// <param name="connectTimeoutMillis"></param> /// <param name="peerConnection"></param> /// <param name="timeoutHandler"></param> private async Task HandleRconAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler) { message.SetKeepAlive(true); Logger.Debug("Initiate reverse connection setup to peer with address {0}.", message.Recipient); var rconMessage = CreateRconMessage(message); // TODO works? // cache the original message until the connection is established _cachedRequests.AddOrUpdate(message.MessageId, tcsResponse, (i, source) => tcsResponse); // wait for response (whether the reverse connection setup was successful) var tcsRconResponse = new TaskCompletionSource<Message.Message>(rconMessage); // .NET-specific: specify and use a RconInboundHandler class var rconInboundHandler = new RconInboundHandler(tcsRconResponse, tcsResponse); // send reverse connection request instead of normal message await SendTcpAsync(rconInboundHandler, tcsRconResponse, rconMessage, channelCreator, connectTimeoutMillis, connectTimeoutMillis, peerConnection); }
/// <summary> /// // TODO document /// </summary> /// <param name="handler"></param> /// <param name="tcsResponse"></param> /// <param name="message"></param> /// <param name="channelCreator"></param> /// <param name="idleTcpSeconds"></param> /// <param name="connectTimeoutMillis"></param> /// <param name="peerConnection"></param> /// <param name="timeoutHandler"></param> private async Task HandleRelayAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, Message.Message message, ChannelCreator channelCreator, int idleTcpSeconds, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler) { var taskPingDone = PingFirst(message.Recipient.PeerSocketAddresses); await taskPingDone; if (!taskPingDone.IsFaulted) { var recipient = PeerSocketAddress.CreateSocketTcp(taskPingDone.Result); var channel = SendTcpCreateChannel(recipient, channelCreator, peerConnection, handler, timeoutHandler, connectTimeoutMillis); await AfterConnectAsync(tcsResponse, message, channel, handler == null); // TODO add this before AfterConnect? var taskResponse = tcsResponse.Task; await taskResponse; if (taskResponse.IsFaulted) { if (taskResponse.Result != null && taskResponse.Result.Type != Message.Message.MessageType.User1) { // "clearInactivePeerSocketAddress" var tmp = new List<PeerSocketAddress>(); foreach (var psa in message.Recipient.PeerSocketAddresses) { if (psa != null) { if (!psa.Equals(taskPingDone.Result)) { tmp.Add(psa); } } } message.SetPeerSocketAddresses(tmp); await SendTcpAsync(handler, tcsResponse, message, channelCreator, idleTcpSeconds, connectTimeoutMillis, peerConnection); } } } else { // .NET-specific: tcsResponse.SetException(new TaskFailedException("No relay could be contacted. <-> " + taskPingDone.Exception)); } }
private ITcpClientChannel SendTcpPeerConnection(PeerConnection peerConnection, IChannelHandler handler, ChannelCreator channelCreator, TaskCompletionSource<Message.Message> tcsResponse) { // if the channel gets closed, the future should get notified var channel = peerConnection.Channel; // channel creator can be null if we don't need to create any channels if (channelCreator != null) { // TODO this doesn't do anything yet channelCreator.SetupCloseListener(channel, tcsResponse); } // we need to replace the handler if this comes from the peer that created a peer connection, // otherwise we need to add a handler AddOrReplace(channel.Pipeline, "dispatcher", "handler", handler); // TODO uncommented Java stuff needed? return channel as ITcpClientChannel; // TODO this will fail if its a server channel!!! }
/// <summary> /// Needs 3 connections. Cleans up channel creator, which means they will be released. /// </summary> /// <param name="tcsDiscover"></param> /// <param name="peerAddress"></param> /// <param name="cc"></param> /// <param name="configuration"></param> private void Discover(TcsDiscover tcsDiscover, PeerAddress peerAddress, ChannelCreator cc, IConnectionConfiguration configuration) { _peer.PingRpc.AddPeerReachableListener(new DiscoverPeerReachableListener(tcsDiscover)); var taskResponseTcp = _peer.PingRpc.PingTcpDiscoverAsync(peerAddress, cc, configuration, SenderAddress); taskResponseTcp.ContinueWith(taskResponse => { var serverAddress = _peer.PeerBean.ServerPeerAddress; if (!taskResponse.IsFaulted) { var tmp = taskResponseTcp.Result.NeighborsSet(0).Neighbors; tcsDiscover.SetReporter(taskResponseTcp.Result.Sender); if (tmp.Count == 1) { var seenAs = tmp.First(); Logger.Info("This peer is seen as {0} by peer {1}. This peer sees itself as {2}.", seenAs, peerAddress, _peer.PeerAddress.InetAddress); if (!_peer.PeerAddress.InetAddress.Equals(seenAs.InetAddress)) { // check if we have this interface on that we can listen to var bindings = new Bindings().AddAddress(seenAs.InetAddress); var status = DiscoverNetworks.DiscoverInterfaces(bindings); Logger.Info("2nd interface discovery: {0}.", status); if (bindings.FoundAddresses.Count > 0 && bindings.FoundAddresses.Contains(seenAs.InetAddress)) { serverAddress = serverAddress.ChangeAddress(seenAs.InetAddress); _peer.PeerBean.SetServerPeerAddress(serverAddress); Logger.Info("This peer had the wrong interface. Changed it to {0}.", serverAddress); } else { // now we know our internal IP, where we receive packets var ports = _peer.ConnectionBean.ChannelServer.ChannelServerConfiguration.PortsForwarding; if (ports.IsManualPort) { serverAddress = serverAddress.ChangePorts(ports.TcpPort, ports.UdpPort); serverAddress = serverAddress.ChangeAddress(seenAs.InetAddress); _peer.PeerBean.SetServerPeerAddress(serverAddress); Logger.Info("This peer had manual ports. Changed it to {0}.", serverAddress); } else { // we need to find a relay, because there is a NAT in the way tcsDiscover.SetExternalHost( "We are most likely behind a NAT. Try to UPNP, NAT-PMP or relay " + peerAddress, taskResponseTcp.Result.Recipient.InetAddress, seenAs.InetAddress); return; } } } // else -> we announce exactly how the other peer sees us var taskResponse1 = _peer.PingRpc.PingTcpProbeAsync(peerAddress, cc, configuration); taskResponse1.ContinueWith(tr1 => { if (tr1.IsFaulted) { tcsDiscover.SetException(new TaskFailedException("TcsDiscover (2): We need at least the TCP connection.", tr1)); } }); var taskResponse2 = _peer.PingRpc.PingUdpProbeAsync(peerAddress, cc, configuration); taskResponse2.ContinueWith(tr2 => { if (tr2.IsFaulted) { Logger.Warn("TcsDiscover (2): UDP failed connection."); } }); // from here we probe, set the timeout here tcsDiscover.Timeout(serverAddress, _peer.ConnectionBean.Timer, DiscoverTimeoutSec); return; } tcsDiscover.SetException(new TaskFailedException(String.Format("Peer {0} did not report our IP address.", peerAddress))); } else { tcsDiscover.SetException(new TaskFailedException("TcsDiscover (1): We need at least the TCP connection.", taskResponse)); } }); }
private async Task ConnectAndSendAsync(IInboundHandler handler, TaskCompletionSource<Message.Message> tcsResponse, ChannelCreator channelCreator, int connectTimeoutMillis, PeerConnection peerConnection, TimeoutFactory timeoutHandler, Message.Message message) { var recipient = message.Recipient.CreateSocketTcp(); var channel = SendTcpCreateChannel(recipient, channelCreator, peerConnection, handler, timeoutHandler, connectTimeoutMillis); await AfterConnectAsync(tcsResponse, message, channel, handler == null); }
/// <summary> /// Creates a channel creator for short-lived connections. /// Always call <see cref="ChannelCreator.ShutdownAsync"/> to release all resources. /// (This needs to be done in any case, whether it succeeds or fails.) /// </summary> /// <param name="permitsUdp">The number of short-lived UDP connections.</param> /// <param name="permitsTcp">The number of short-lived TCP connections.</param> /// <returns>The future channel creator.</returns> public Task<ChannelCreator> CreateAsync(int permitsUdp, int permitsTcp) { if (permitsUdp > _maxPermitsUdp) { throw new ArgumentException(String.Format("Cannot acquire more UDP connections ({0}) than maximally allowed ({1}).", permitsUdp, _maxPermitsUdp)); } if (permitsTcp > _maxPermitsTcp) { throw new ArgumentException(String.Format("Cannot acquire more TCP connections ({0}) than maximally allowed ({1}).", permitsTcp, _maxPermitsTcp)); } var tcsChannelCreator = new TaskCompletionSource<ChannelCreator>(); _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); return tcsChannelCreator.Task; } var tcsChannelCreationDone = new TaskCompletionSource<object>(); tcsChannelCreationDone.Task.ContinueWith(delegate { // release the permits in all cases // otherwise, we may see inconsistencies //Console.WriteLine("Reservation ({0}): A CC shut down. Releasing {1} UDP, {2} TCP permits.", RuntimeHelpers.GetHashCode(this), permitsUdp, permitsTcp); _semaphoreUdp.Release2(permitsUdp); _semaphoreTcp.Release2(permitsTcp); }); // instead of Executor.execute(new WaitReservation()) _singleThreadTaskFactory.StartNew(delegate { //Console.WriteLine("Reservation ({0}): Executing async reservation...", RuntimeHelpers.GetHashCode(this)); // Creates a reservation that returns a channel creator in a // task, once we have the semaphore. // Tries to reserve a channel creator. If too many channels are already // created, wait until channels are closed. ChannelCreator channelCreator; _readWriteLock.EnterReadLock(); try { if (_shutdown) { tcsChannelCreator.SetException(new TaskFailedException("Shutting down.")); return; } try { //Console.Write("[{0}] Reservation ({1}): Acquiring {2} UDP permits.", Thread.CurrentThread.ManagedThreadId, RuntimeHelpers.GetHashCode(this), permitsUdp); _semaphoreUdp.Acquire(permitsUdp); //Console.Write("({0}) --> granted\n", RuntimeHelpers.GetHashCode(this)); } catch (Exception ex) { tcsChannelCreator.SetException(ex); return; } try { _semaphoreTcp.Acquire(permitsTcp); } catch (Exception ex) { _semaphoreUdp.Release(permitsUdp); tcsChannelCreator.SetException(ex); return; } channelCreator = new ChannelCreator(tcsChannelCreationDone, permitsUdp, permitsTcp, _channelClientConfiguration); AddToSet(channelCreator); } finally { _readWriteLock.ExitReadLock(); } tcsChannelCreator.SetResult(channelCreator); }); return tcsChannelCreator.Task; } finally { _readWriteLock.ExitReadLock(); } }
private ITcpClientChannel SendTcpCreateChannel(IPEndPoint recipient, ChannelCreator channelCreator, PeerConnection peerConnection, IChannelHandler handler, TimeoutFactory timeoutHandler, int connectTimeoutMillis) { // create pipeline var handlers = new Dictionary<string, IChannelHandler>(); if (timeoutHandler != null) { handlers.Add("timeout0", timeoutHandler.CreateIdleStateHandlerTomP2P()); handlers.Add("timeout1", timeoutHandler.CreateTimeHandler()); } handlers.Add("decoder", new TomP2PCumulationTcp(ChannelClientConfiguration.SignatureFactory)); handlers.Add("encoder", new TomP2POutbound(false, ChannelClientConfiguration.SignatureFactory)); if (peerConnection != null) { // we expect responses on this connection handlers.Add("dispatcher", _dispatcher); } if (timeoutHandler != null) { handlers.Add("handler", handler); } HeartBeat heartBeat = null; if (peerConnection != null) { heartBeat = new HeartBeat(peerConnection.HeartBeatMillis, PingBuilderFactory); handlers.Add("heartbeat", heartBeat); } var channel = channelCreator.CreateTcp(recipient, connectTimeoutMillis, handlers); if (peerConnection != null && channel != null) { peerConnection.SetChannel(channel); heartBeat.SetPeerConnection(peerConnection); } return channel; }
/// <summary> /// Broadscasts a UDP message (layer 2) and expects a response. /// </summary> /// <param name="channelCreator">The channel creator will create a UDP connection.</param> /// <returns>The future task that was added in the constructor.</returns> public Task <Message.Message> SendBroadcastUdpAsync(ChannelCreator channelCreator) { var sendTask = ConnectionBean.Sender.SendUdpAsync(this, _tcsResponse, _message, channelCreator, IdleUdpSeconds, true); return(ExecuteAsync(sendTask)); }