Search This Blog

Sunday, October 21, 2012

Using fastJSON serializer with Eneter

Summary: Simple example showing how to use custom serializers for interprocess communication.

Introduction

The example bellow demonstrates how to use your own custom serializer for interporcess communication.
The example implements a simple client-service communication. The service is a console application calculating numbers and the client is a simple desktop application using the service for the calculation and displaying results.

The communication is based on TCP and uses fastJSON for serialization of messages.

The whole example can be downloaded from here.

The example bellow uses Eneter Messaging Framework providing easy to use functionality for interprocess communication.
(The framework is free for non-commercial use and can be downloaded from http://www.eneter.net/ProductDownload.htm. You need to download Eneter for .NET.
More technical details can be found at technical info.)

fastJSON

fastJSON is a nice and popular open source library providing fast JSON serializing functionality. More details and the latest version of this library can be found here:
http://www.codeproject.com/Articles/159450/fastJSON
http://fastjson.codeplex.com/

Serialization in Eneter Framework

Eneter Messaging Framework uses serialization to convert data structures to messages and deserialization to restore received messages into data structures.
Although the framework provides several serializers you may need to use some other serializer for the communication. In such case you can provide your own serializer by implementing ISerializer
interface which is declared in Eneter framework.

The interface is very simple. It consists of two methods:

// The interface declares the API for serialization and deserialization.
// Eneter framework uses this interface for serializing messages.
public interface ISerializer
{
    // Serializes data. 
    // Returns serialized data: returned type can be byte[] or string.
    object Serialize<t>(T dataToSerialize);

    // Deserializes data.
    T Deserialize<t>(object serializedData);
}

fastJSON as Custom Serializer in Eneter

The following code shows how to implement ISerializer interface to use fastJSON functionality to serialize / deserialize messages.

The implementation is very simple:

using Eneter.Messaging.DataProcessing.Serializing;
using fastJSON;

namespace FastJSONForEneter
{
    /// <summary>
    /// Implements customized serializer/deserializer for Eneter Framework
    /// using fastJSON library.
    /// 
    /// More info about fastJSON can be found here:
    /// http://www.codeproject.com/Articles/159450/fastJSON
    /// </summary>
    public class FastJSONSerializer : ISerializer
    {
        private JSONParameters myJsonParameters;

        public FastJSONSerializer()
        {
            myJsonParameters = new JSONParameters();
            
            // Set extensions to false.
            // Note: This turns off fully qualified names.
            //       It allows to serialize/deserialize types
            //       declared in different assemblies.
            //       E.g. the message data type can be declared separately
            //       in server and client.
            myJsonParameters.UseExtensions = false;
        }

        public FastJSONSerializer(JSONParameters jsonParameters)
        {
            myJsonParameters = jsonParameters;
        }

        public _T Deserialize<_T>(object serializedData)
        {
            return JSON.Instance.ToObject<_T>((string)serializedData);
        }

        public object Serialize<_T>(_T dataToSerialize)
        {
            return JSON.Instance.ToJSON(dataToSerialize, myJsonParameters);
        }
    }
}

Service Using Custom fastJSON Serializer

The service is a simple console application calculating two numbers. It listens on a specified TCP port and uses our custom fastJSON to deserialize received messages and to serialize response messages.
The custom serializer is injected to the DuplexTypedMessagesFactory. The factory then creates the message receiver that uses this serializer.

using System;
using Eneter.Messaging.DataProcessing.Serializing;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using FastJSONForEneter;


namespace SimpleService
{
    // Request message coming from the cient.
    public class RequestMessage
    {
        public int Number1;
        public int Number2;
    }

    // Response message.
    public class ResponseMessage
    {
        public int Result;
    }

    class Program
    {
        static void Main(string[] args)
        {
            // We want to use JSON serializer based on fastJSON.
            ISerializer aSerializer = new FastJSONSerializer();

            // Create message receiver using JSON.
            // It receives 'RequestMessage' and responses result in 'int'.
            IDuplexTypedMessagesFactory aReceiverFactory = new DuplexTypedMessagesFactory(aSerializer);
            var aReceiver = aReceiverFactory.CreateDuplexTypedMessageReceiver<ResponseMessage, RequestMessage>();

            // Subscribe to receive response messages.
            aReceiver.MessageReceived += OnMessageReceived;

            // Use TCP messaging for the communication.
            IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
            IDuplexInputChannel anInputChannel =
                aMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:8076/");

            // Attach the input channel to the message receiver and start listening.
            aReceiver.AttachDuplexInputChannel(anInputChannel);

            Console.WriteLine("Simple service is running. Press enter to stop.");
            Console.ReadLine();

            // Detach the input channel to stop listening.
            // (it releases the listening thread)
            aReceiver.DetachDuplexInputChannel();
        }

        private static void OnMessageReceived(object eventSender, TypedRequestReceivedEventArgs<RequestMessage> e)
        {
            // Calculate received numbers and sends back the response.
            int aResult = e.RequestMessage.Number1 + e.RequestMessage.Number2;

            Console.WriteLine("{0} + {1} = {2}", e.RequestMessage.Number1, e.RequestMessage.Number2, aResult);

            // Send back the response message.
            ResponseMessage aResponseMessage = new ResponseMessage();
            aResponseMessage.Result = aResult;

            var aReceiver = (IDuplexTypedMessageReceiver<ResponseMessage, RequestMessage>)eventSender;
            aReceiver.SendResponseMessage(e.ResponseReceiverId, aResponseMessage);
        }
    }
}

Client Using Custom fastJSON Serializer

The client is a simple desktop application allowing user to enter two numbers. When the button is pressed the client sends the request message serialized with our custom fastJSON serializer.
Then when the response is received it displays the result.
The custom serializer is injected to the DuplexTypedMessagesFactory. The factory then creates message sender which uses this serializer.

using System;
using System.Windows.Forms;
using Eneter.Messaging.DataProcessing.Serializing;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using FastJSONForEneter;

namespace SimpleClient
{
    public partial class Form1 : Form
    {
        // Request message coming from the cient.
        public class RequestMessage
        {
            public int Number1;
            public int Number2;
        }

        // Response message.
        public class ResponseMessage
        {
            public int Result;
        }

        private IDuplexTypedMessageSender<ResponseMessage, RequestMessage> mySender;

        public Form1()
        {
            InitializeComponent();

            // We want to use JSON serializer based on fastJSON.
            ISerializer aSerializer = new FastJSONSerializer();

            // Create message sender using JSON.
            // It sends 'RequestMessage' and  receives 'int'.
            IDuplexTypedMessagesFactory aSenderFactory = new DuplexTypedMessagesFactory(aSerializer);
            mySender = aSenderFactory.CreateDuplexTypedMessageSender<ResponseMessage, RequestMessage>();

            // Subscribe to receive responses.
            mySender.ResponseReceived += OnResponseReceived;

            // Use TCP messaging for the communication.
            IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
            IDuplexOutputChannel anOutputChannel =
                aMessaging.CreateDuplexOutputChannel("tcp://127.0.0.1:8076/");

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

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Detach the output channel to stop the listening thread.
            mySender.DetachDuplexOutputChannel();
        }

        private void SendBtn_Click(object sender, EventArgs e)
        {
            // Create the request message.
            RequestMessage aRequestMessage = new RequestMessage();
            aRequestMessage.Number1 = int.Parse(Number1TextBox.Text);
            aRequestMessage.Number2 = int.Parse(Number2TextBox.Text);

            // Send the request message to the service
            // to calculate numbers.
            mySender.SendRequestMessage(aRequestMessage);
        }


        private void OnResponseReceived(object sender, TypedResponseReceivedEventArgs<ResponseMessage> e)
        {
            // Display the received result.
            // But invoke the displaying in the UI thread.
            UI(() => ResultTextBox.Text = e.ResponseMessage.Result.ToString());
        }


        // Helper method to invoke some functionality in UI thread.
        private void UI(Action uiMethod)
        {
            // If we are not in the UI thread then we must synchronize via the invoke mechanism.
            if (InvokeRequired)
            {
                Invoke(uiMethod);
            }
            else
            {
                uiMethod();
            }
        }
    }
}





No comments:

Post a Comment