public override void Read(ChannelHandlerContext ctx, object msg) { // client-side: // Here, the result for the awaitable task can be set. // If it is not a fire-and-forget message, the "result" of the TCS // must be set here. (If FF, then it is set in Sender.) // Java uses a SimpleChannelInboundHandler that only expects Message objects var responseMessage = msg as Message.Message; if (responseMessage == null) { return; } var recvMessageId = new MessageId(responseMessage); // error handling if (responseMessage.Type == Message.Message.MessageType.UnknownId) { string msg2 = "Message was not delivered successfully. Unknown ID (peer may be offline or unknown RPC handler): " + _message; ExceptionCaught(ctx, new PeerException(PeerException.AbortCauseEnum.PeerAbort, msg2)); return; } if (responseMessage.Type == Message.Message.MessageType.Exception) { string msg2 = "Message caused an exception on the other side. Handle as PeerAbort: " + _message; ExceptionCaught(ctx, new PeerException(PeerException.AbortCauseEnum.PeerAbort, msg2)); return; } if (responseMessage.IsRequest()) { ctx.FireRead(responseMessage); return; } if (!_sendMessageId.Equals(recvMessageId)) { string msg2 = String.Format( "Response message [{0}] sent to the node is not the same as we expect. We sent request message [{1}].", responseMessage, _message); ExceptionCaught(ctx, new PeerException(PeerException.AbortCauseEnum.PeerAbort, msg2)); return; } // We need to exclude RCON messages from the sanity check because we // use this RequestHandler for sending a Type.REQUEST_1, // RPC.Commands.RCON message on top of it. Therefore the response // type will never be the same Type as the one the user initially // used (e.g. DIRECT_DATA). if (responseMessage.Command != Rpc.Rpc.Commands.Rcon.GetNr() && _message.Recipient.IsRelayed != responseMessage.Sender.IsRelayed) { string msg2 = String.Format( "Response message [{0}] sent has a different relay flag than we sent with request message [{1}]. Recipient ({2}) / Sender ({3}).", responseMessage, _message, _message.Recipient.IsRelayed, responseMessage.Sender.IsRelayed); ExceptionCaught(ctx, new PeerException(PeerException.AbortCauseEnum.PeerAbort, msg2)); return; } // we got a good answer, let's mark the sender as alive if (responseMessage.IsOk() || responseMessage.IsNotOk()) { lock (PeerBean.PeerStatusListeners) { if (responseMessage.Sender.IsRelayed && responseMessage.PeerSocketAddresses.Count != 0) { // use the response message as we have up-to-date content for the relays PeerAddress remotePeer = responseMessage.Sender.ChangePeerSocketAddresses(responseMessage.PeerSocketAddresses); responseMessage.SetSender(remotePeer); } foreach (IPeerStatusListener listener in PeerBean.PeerStatusListeners) { listener.PeerFound(responseMessage.Sender, null, null); } } } // call this for streaming support if (!responseMessage.IsDone) { Logger.Debug("Good message is streaming. {0}", responseMessage); return; } if (!_message.IsKeepAlive()) { Logger.Debug("Good message {0}. Close channel {1}.", responseMessage, ctx.Channel); // .NET-specific: // set the result now, but trigger the notify when the channel is closed _tcsResponse.ResponseLater(responseMessage, ctx); // in Java, the channel creator adds a listener that sets the // future response result when the channel is closed ctx.Close(); // TODO needed? } else { Logger.Debug("Good message {0}. Leave channel {1} open.", responseMessage, ctx.Channel); _tcsResponse.SetResult(responseMessage); } }