Search This Blog

Tuesday, July 6, 2010

Request-Response Communication

Summary: Simple example showing how to implement the request-response communication between applications.


The request-response communication is a communication scenario where messages flow in both directions - request messages from the sender to the receiver and response messages from the receiver back to the sender.

The example bellow shows the server receiving messages and sending responses back to the client. It also shows how to implement the client sending messages to the server and receiving response messages.

1. Add Eneter.Messaging.Framework.dll to use the messaging functionality in your .Net applications. (Full, not limited and for non-commercial usage free version of the framework can be downloaded from www.eneter.net.)

2. Define message types.
For the request-response communication you must define type of the request message and type of the response message.
And same as described in the previous part, you should consider what serialization will be used.
(Because the serialization can require some additional attributes in the declarations.)

In our example we will use the binary serialization. The request message type is 'Person' and the response type is the string.

using System;
using CommonTypes;
using Eneter.Messaging.DataProcessing.Serializing;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.NamedPipeMessagingSystem;

namespace DuplexTypedMessageServer
{
    class Program
    {
        // Duplex typed message receicer that receives type Person and responses type string.
        static IDuplexTypedMessageReceiver<string, Person> myMessageReceiver;

        static void Main(string[] args)
        {
            // Create factory to create input channel based on Named Pipes
            IMessagingSystemFactory aMessagingSystemFactory = new NamedPipeMessagingSystemFactory();
            IDuplexInputChannel anDuplexInputChannel =
                aMessagingSystemFactory.CreateDuplexInputChannel("//127.0.0.1/MyPipeName3");

            // Create duplex typed message receiver using the binary serialization.
            ISerializer aSerializer = new BinarySerializer();
            IDuplexTypedMessagesFactory aDuplexTypedMessagesFactory =
                new DuplexTypedMessagesFactory(aSerializer);
            myMessageReceiver = aDuplexTypedMessagesFactory.CreateDuplexTypedMessageReceiver<string, Person>();
            myMessageReceiver.MessageReceived += OnMessageReceived;

            // Attach the input channel to the string message receiver and start listening.
            myMessageReceiver.AttachDuplexInputChannel(anDuplexInputChannel);

            Console.WriteLine("The service is listening. Press enter to stop.\n");
            Console.ReadLine();

            myMessageReceiver.DetachDuplexInputChannel();
        }

        static void OnMessageReceived(object sender, TypedRequestReceivedEventArgs<Person> e)
        {
            Console.WriteLine("Received Message:");
            Console.WriteLine("Name: " + e.RequestMessage.Name);
            Console.WriteLine("Number of items: " + e.RequestMessage.NumberOfItems.ToString());
            Console.WriteLine();

            // As a response send the current time.
            string aResponseMessage = DateTime.Now.ToString();
            myMessageReceiver.SendResponseMessage(e.ResponseReceiverId, aResponseMessage);
        }
    }
}

4. Implement the client.
The client bellow sends the message type of 'Person' and receives the response message type of string.

using System;
using System.Windows.Forms;
using CommonTypes;
using Eneter.Messaging.DataProcessing.Serializing;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.NamedPipeMessagingSystem;

namespace DuplexTypedMessageClient
{
    public partial class Form1 : Form
    {
        // Duplex typed message sender that sends type Person and
        // receives responses of type string.
        private IDuplexTypedMessageSender<string, Person> myMessageSender;

        public Form1()
        {
            InitializeComponent();

            OpenConnection();
        }

        private void OpenConnection()
        {
            // Create factory to create input channel based on Named Pipes
            IMessagingSystemFactory aMessagingSystemFactory = new NamedPipeMessagingSystemFactory();
            IDuplexOutputChannel anOutputChannel =
                aMessagingSystemFactory.CreateDuplexOutputChannel("//127.0.0.1/MyPipeName3");

            // Create duplex typed message sender that uses the binnary serializer.
            ISerializer aSerializer = new BinarySerializer();
            IDuplexTypedMessagesFactory aDuplexTypedMessagesFactory =
                new DuplexTypedMessagesFactory(aSerializer);
            myMessageSender = aDuplexTypedMessagesFactory.CreateDuplexTypedMessageSender<string, Person>();
            myMessageSender.ResponseReceived += OnResponseReceived;

            // Attach duplex output channel and be able to send messages
            // and receive response messages.
            myMessageSender.AttachDuplexOutputChannel(anOutputChannel);
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Correctly close connection.
            // Otherwise the thread listening to response messages will leak.
            myMessageSender.DetachDuplexOutputChannel();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            // Prepare the message.
            Person aPerson = new Person();
            aPerson.Name = NameTextBox.Text;
            aPerson.NumberOfItems = int.Parse(NumberOfItemsTextBox.Text);

            // Send the message.
            myMessageSender.SendRequestMessage(aPerson);
        }

        private void OnResponseReceived(object sender, TypedResponseReceivedEventArgs<string> e)
        {
            string aResponseMessage = e.ResponseMessage;
            InvokeInUIThread(() => ReceivedResponseTextBox.Text = aResponseMessage);
        }

        // Helper method to invoke the given delegate
        // in the UI thread.
        private void InvokeInUIThread(Action action)
        {
            if (InvokeRequired)
            {
                Invoke(action);
            }
            else
            {
                action();
            }
        }
    }
}

And here are communicating applications.

No comments:

Post a Comment