/// <summary> /// Creates the handler pipeline. After this, it passes the user-set pipeline /// filter where the handlers can be modified. /// </summary> /// <param name="isTcp"></param> private Pipeline GetPipeline(bool isTcp) { var timeoutFactory = new TimeoutFactory(null, ChannelServerConfiguration.IdleTcpSeconds, _peerStatusListeners, "Server"); var pipeline = new Pipeline(); if (isTcp) { pipeline.AddLast("dropconnection", _tcpDropConnectionInboundHandler); pipeline.AddLast("timeout0", timeoutFactory.CreateIdleStateHandlerTomP2P()); pipeline.AddLast("timeout1", timeoutFactory.CreateTimeHandler()); pipeline.AddLast("decoder", _tcpDecoderHandler); } else { // no need for a timeout handler, since whole packet arrives or nothing // different from TCP where the stream can be closed by the remote peer // in the middle of the transmission pipeline.AddLast("dropconnection", _udpDropConnectionInboundHandler); pipeline.AddLast("decoder", _udpDecoderHandler); } pipeline.AddLast("encoder", _encoderHandler); pipeline.AddLast("dispatcher", _dispatcher); var filteredPipeline = ChannelServerConfiguration.PipelineFilter.Filter(pipeline, isTcp, false); return(filteredPipeline); }
public void ResponseFireAndForget() { Logger.Debug("The reply handler was a fire-and-forget handler. No message is sent back for {0}.", _requestMessage); if (!_ctx.Channel.IsUdp) { const string msg = "There is no TCP fire-and-forget. Use UDP in that case. "; Logger.Warn(msg + _requestMessage); throw new SystemException(msg); } TimeoutFactory.RemoveTimeout(_ctx); }
/// <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> /// 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> /// 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(); }
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); }
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); }
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; }
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> /// // 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> /// 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); }