Search This Blog

Sunday, July 4, 2010

One-Way Communication

Summary: Simple example showing how to implement the one-way communication between applications.


The one-way communication is a communication scenario where messages flow only in one direction - from sender to receiver.Although the scenario sounds simple it can be quite complex programming if you use just default .Net functionality (e.g. sockets). To simplify the implementation and reduce the effort you would probably consider to use some communication framework.

I would like to show you how the one-way communication with typed messages works with Eneter.Messaging.Framework.
The example shows, how to implement the server receiving messages of the specified type and the client sending messages of the same type.


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
You need to define message types you want to use for the communication. Because the serialization and deserialization (used during sending and receiving) can require some additional attributes in the declaration of the type you also should consider what serialization will be used.

Binary Serialization
The data is serialized into array of bytes. The serialization is fast and consumes less memory as string serializations.
It requires to use 'Serializable' attribute and the data must be declared in an assembly that is linked by the server and also by the client.
(Here you can consider to create some assembly for shared data types.)
The binary serialization is not supported by Silverlight.
The serialization is based on BinaryFormater provided by .Net platform.

Xml Serialization
The data is serialized into the string. The serialization is slower and consumes more memory as the binary serialization.
It does not require, the server and the client link the same assembly with data types. The data can be declared separately on both sides.
The xml serialization works in Silverlight too.
The serialization is based on XmlSerializer provided by .Net platform..

Data Contract Serialization
The data is serialized as Xml into the string. The serialization is based DataContractSerializer provided by .Net platform. Therefore, you can use all data contract attributes to declare the data for the serialization / deserialization.

Our example uses the binary serialization, therefore here is the declaration for our message.

using System;

namespace CommonTypes
{
    [Serializable]
    public class Person
    {
        public string Name { get; set; }
        public int NumberOfItems { get; set; }
    }
}

3. Implement the server
When you implement the interprocess communication you must choose if you want to communicate via Named Pipes, Tcp or Http protocol.

Named Pipes
// Create factory for messaging via Named Pipes.
IMessagingSystemFactory aMessagingSystemFactory =
new NamedPipeMessagingSystemFactory();

// Create input channel for listening to messages.
IInputChannel anInputChannel =
aMessagingSystemFactory.CreateInputChannel("//127.0.0.1/MyPipeName2");

Tcp
// Create factory for messaging via Tcp.
IMessagingSystemFactory aMessagingSystemFactory =
new TcpMessagingSystemFactory();

// Create input channel for listening to messages.
IInputChannel anInputChannel = aMessagingSystemFactory.CreateInputChannel("127.0.0.1:8091");

Http
// Create factory for messaging via Http.
IMessagingSystemFactory aMessagingSystemFactory =
new HttpMessagingSystemFactory();

// Create input channel for listening to messages.
IInputChannel anInputChannel =
aMessagingSystemFactory.CreateInputChannel("127.0.0.1:8091");

Let's use Named Pipes in our example.


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 TypedMessageServer
{
    class Program
    {
        // Strong type receiver for the type Person.
        static private ITypedMessageReceiver<Person> myReceiver;

        static void Main(string[] args)
        {
            // Create receiver for the message type 'Person' with binary serializer.
            ISerializer aSerializer = new BinarySerializer();
            ITypedMessagesFactory aTypedMessagesFactory = new TypedMessagesFactory(aSerializer);
            myReceiver = aTypedMessagesFactory.CreateTypedMessageReceiver<Person>();
            myReceiver.MessageReceived += OnMessageReceived;

            // Create input channel listening to Named Pipe.
            // Note: This is a one-way channel. Therefore, it can just receive messages.
            // Note: Do not use '/' at the end of the address.
            IMessagingSystemFactory aMessaging = new NamedPipeMessagingSystemFactory();
            IInputChannel anInputChannel = aMessaging.CreateInputChannel("//127.0.0.1/MyPipeName2");

            // Attach input channel ans start listening.
            myReceiver.AttachInputChannel(anInputChannel);

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

            myReceiver.DetachInputChannel();
        }

        static void OnMessageReceived(object sender, TypedMessageReceivedEventArgs<Person> e)
        {
            // If there was a problem with the receiving.
            if (e.ReceivingError != null)
            {
                Console.WriteLine("Detected problem during receiving: " + e.ReceivingError.Message);
            }
            else
            {
                Console.WriteLine("Received Message:");
                Console.WriteLine("Name: " + e.MessageData.Name);
                Console.WriteLine("Number of items: " + e.MessageData.NumberOfItems.ToString());
                Console.WriteLine();
            }
        }
    }
}


4. Implement the client
And here is the implementation for the client sending messages of type Person.

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 TypedMessageClient
{
    public partial class Form1 : Form
    {
        private ITypedMessageSender<Person> myMessageSender;

        public Form1()
        {
            InitializeComponent();

            // Create input channel listening to Named Pipe.
            // Note: This is a one-way channel. Therefore, it can just receive messages.
            IMessagingSystemFactory aMessaging = new NamedPipeMessagingSystemFactory();
            IOutputChannel anOutputChannel = aMessaging.CreateOutputChannel("//127.0.0.1/MyPipeName2");

            // Create the message sender sending the class of type 'Person'.
            // The sender will use the binary serializer.
            ISerializer aSerializer = new BinarySerializer();
            ITypedMessagesFactory aTypedMessagesFactory = new TypedMessagesFactory(aSerializer);
            myMessageSender = aTypedMessagesFactory.CreateTypedMessageSender<Person>();

            // Attach the output channel and be able to send messages.
            myMessageSender.AttachOutputChannel(anOutputChannel);
        }

        private void SendButton_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.SendMessage(aPerson);
        }
    }
}

And here are communicating applications.


2 comments:

  1. Hi Ondrej,

    I am trying to implement this simple one way communication example on my laptop. However, I am getting the following errors when I try to build:

    On the Client Side:

    (a) Type or namespace name could not be found (Directive or assembly reference missing error for following keywords):

    ITypedMessageSender, IOutputChannel, ITypedMessagesFactory, TypedMessagesFactory, NameTextBox, NumberOfItemsTextBox, Drawing does not exist in System.

    (b)Eneter.Messaging.MessagingSystems.MessagingSystemBase.IMessagingSystemFactory' does not contain a definition for 'CreateOutputChannel' and no extension method 'CreateOutputChannel' accepting a first argument of type

    'Eneter.Messaging.MessagingSystems.MessagingSystemBase.IMessagingSystemFactory' could be found (are you missing a using directive or an assembly reference?)

    On the Server Side:

    (a) Type or namespace name could not be found (Directive or assembly reference missing error for following keywords):

    ITypedMessageReceiver, ITypedMessagesFactory, TypedMessagesFactory, IInputChannel, TypedMessageReceivedArgs

    (b)Eneter.Messaging.MessagingSystems.MessagingSystemBase.IMessagingSystemFactory' does not contain a definition for 'CreateInputChannel' and no extension method 'CreateInputChannel' accepting a first argument of type

    'Eneter.Messaging.MessagingSystems.MessagingSystemBase.IMessagingSystemFactory' could be found (are you missing a using directive or an assembly reference?) C:\Users\agrawala\Documents\Visual Studio 2010\Projects\OneWayCommunication\OneWayCommunication\Program.cs

    Please help me to sort these errors so that I can run the example.

    ReplyDelete
    Replies
    1. Hi Abhishek,

      The reason why it does not work is that specialized one-way channels are not supported in Eneter anymore.
      Instead of that you can use normal duplex channels and use them as one-way (duplex channels are capable to do bidirectional communication as well as one-way communication).

      So e.g. instead of IOutputChannel anOutputChannel = aMessaging.CreateOutputChannel("//127.0.0.1/MyPipeName2"); you can write

      IDuplexOutputChannel anOutputChannel = aMessaging.CreateDuplexOutputChannel("//127.0.0.1/MyPipeName2");

      You need to do the same for input channel and typed message sender/recevier (I mean you need to duplex variants of those components.)

      Here is an example which is very similar to the example in this article but uses duplex channels.
      http://eneter.blogspot.de/2012/01/simple-request-response-communication.html

      I hope my response will help.

      Delete