Search This Blog

Saturday, November 10, 2012

Server Exposing Services via one IP Address and Port

Summary: Simple example showing how to implement a server exposing multiple services via one IP address and port.
The communication is realized via TCP and the implementation is provided for .NET and Android.

Introduction

The example bellow demonstrates how to implement a server which exposes its services via one IP address and port.
To demonstrate this scenario, the example implements a simple server which provides four different services to calculate two numbers: Summary, Subtraction, Multiplication and Division. The example also shows the implementation of the client using these services.
The implementation of the client is provided for .NET and Android.

The source code for this example can be downloaded from here.

The example bellow uses Eneter Messaging Framework that makes the whole communication very simple.
(The framework is free for non-commercial use and can be downloaded from http://www.eneter.net. You need to download Eneter for.NET and Eneter for Android.
More detailed technical info can be found at technical info.)

Channel Wrapper and Channel Unwrapper


In order to route different types of request messages via one IP address and port the implementation uses the eneter components Duplex Channel Wrapper and Duplex Channel Unwrapper.

The user client code sends the request using the message sender component. It sends the message via the channel to the channel wrapper. The channel wrapper receives the message, wraps it and sends via one output channel. (It wraps messages from multiple channels into one channel.)
The channel unwrapper on the server side receives the wrapped message, unwraps it and forwards via the corresponding output channel to the correct message receiver. The message receiver then notifies the user code to process the incoming request.
The user code processes the request and can send back the response message.



Server Listening via One IP Address and Port


The server is a very simple console application exposing different calculation services via one IP address and port. It uses the channel unwrapper component which receives all incoming messages via one input channel.
When a message is received, it is unwrapped and forwarded to the correct receiver (service).

The implementation is very simple:
using System;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.Infrastructure.ConnectionProvider;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using Eneter.Messaging.MessagingSystems.ThreadPoolMessagingSystem;
using Eneter.Messaging.Nodes.ChannelWrapper;

namespace ServerCalculator2
{
    // Input data for calculator requests
    public class CalculatorInputData
    {
        public double Number1 { get; set; }
        public double Number2 { get; set; }
    }

    // Output result from the calculator
    public class CalculatorOutputData
    {
        public double Result { get; set; }
    }

    internal class Calculator
    {
        public Calculator()
        {
            // Internal messaging used for messaging between channel unwrapper
            // and typed message receivers.
            // We want that requests do not block each other. So every request will be processed in its own thread.
            IMessagingSystemFactory anInternalMessaging = new ThreadPoolMessagingSystemFactory();

            // All messages are received via one channel. So we must provide "unwrapper" forwarding incoming messages
            // to correct receivers.
            IChannelWrapperFactory aChannelWrapperFactory = new ChannelWrapperFactory();
            myDuplexChannelUnwrapper = aChannelWrapperFactory.CreateDuplexChannelUnwrapper(anInternalMessaging);

            // To connect receivers and the unwrapper with duplex channels we can use the following helper class.
            IConnectionProviderFactory aConnectionProviderFactory = new ConnectionProviderFactory();
            IConnectionProvider aConnectionProvider = aConnectionProviderFactory.CreateConnectionProvider(anInternalMessaging);

            // Factory to create message receivers.
            IDuplexTypedMessagesFactory aMessageReceiverFactory = new DuplexTypedMessagesFactory();
            
            // Create receiver to sum two numbers.
            mySumReceiver = aMessageReceiverFactory.CreateDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData>();
            mySumReceiver.MessageReceived += SumCmd; // attach method handling the request
            aConnectionProvider.Attach(mySumReceiver, "Sum"); // attach the input channel to get messages from unwrapper

            // Receiver to subtract two numbers.
            mySubtractReceiver = aMessageReceiverFactory.CreateDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData>();
            mySubtractReceiver.MessageReceived += SubCmd; // attach method handling the request
            aConnectionProvider.Attach(mySubtractReceiver, "Sub"); // attach the input channel to get messages from unwrapper

            // Receiver for multiply two numbers.
            myMultiplyReceiver = aMessageReceiverFactory.CreateDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData>();
            myMultiplyReceiver.MessageReceived += MulCmd; // attach method handling the request
            aConnectionProvider.Attach(myMultiplyReceiver, "Mul"); // attach the input channel to get messages from unwrapper

            // Receiver for divide two numbers.
            myDivideReceiver = aMessageReceiverFactory.CreateDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData>();
            myDivideReceiver.MessageReceived += DivCmd; // attach method handling the request
            aConnectionProvider.Attach(myDivideReceiver, "Div"); // attach the input channel to get messages from unwrapper
        }


        public void Start()
        {
            // We use TCP based messaging.
            IMessagingSystemFactory aServiceMessagingSystem = new TcpMessagingSystemFactory();
            IDuplexInputChannel anInputChannel = aServiceMessagingSystem.CreateDuplexInputChannel("tcp://127.0.0.1:8091/");

            // Attach the input channel to the unwrapper and start to listening.
            myDuplexChannelUnwrapper.AttachDuplexInputChannel(anInputChannel);
        }

        public void Stop()
        {
            // Detach the input channel from the unwrapper and stop listening.
            // Note: It releases listening threads.
            myDuplexChannelUnwrapper.DetachDuplexInputChannel();
        }

        // It is called when a request to sum two numbers was received.
        private void SumCmd(object sender, TypedRequestReceivedEventArgs<CalculatorInputData> e)
        {
            // Get input data.
            CalculatorInputData anInputData = e.RequestMessage;

            // Calculate output result.
            CalculatorOutputData aReturn = new CalculatorOutputData();
            aReturn.Result = anInputData.Number1 + anInputData.Number2;

            Console.WriteLine("{0} + {1} = {2}", anInputData.Number1, anInputData.Number2, aReturn.Result);

            // Response result to the client.
            mySumReceiver.SendResponseMessage(e.ResponseReceiverId, aReturn);
        }

        // It is called when a request to subtract two numbers was received.
        private void SubCmd(object sender, TypedRequestReceivedEventArgs<CalculatorInputData> e)
        {
            // Get input data.
            CalculatorInputData anInputData = e.RequestMessage;

            // Calculate output result.
            CalculatorOutputData aReturn = new CalculatorOutputData();
            aReturn.Result = anInputData.Number1 - anInputData.Number2;

            Console.WriteLine("{0} - {1} = {2}", anInputData.Number1, anInputData.Number2, aReturn.Result);

            // Response result to the client.
            mySubtractReceiver.SendResponseMessage(e.ResponseReceiverId, aReturn);
        }
        

        // It is called when a request to multiply two numbers was received.
        private void MulCmd(object sender, TypedRequestReceivedEventArgs<CalculatorInputData> e)
        {
            // Get input data.
            CalculatorInputData anInputData = e.RequestMessage;

            // Calculate output result.
            CalculatorOutputData aReturn = new CalculatorOutputData();
            aReturn.Result = anInputData.Number1 * anInputData.Number2;

            Console.WriteLine("{0} x {1} = {2}", anInputData.Number1, anInputData.Number2, aReturn.Result);

            // Response result to the client.
            myMultiplyReceiver.SendResponseMessage(e.ResponseReceiverId, aReturn);
        }

        // It is called when a request to divide two numbers was received.
        private void DivCmd(object sender, TypedRequestReceivedEventArgs<CalculatorInputData> e)
        {
            // Get input data.
            CalculatorInputData anInputData = e.RequestMessage;

            // Calculate output result.
            CalculatorOutputData aReturn = new CalculatorOutputData();
            aReturn.Result = anInputData.Number1 / anInputData.Number2;

            Console.WriteLine("{0} / {1} = {2}", anInputData.Number1, anInputData.Number2, aReturn.Result);

            // Response result to the client.
            myDivideReceiver.SendResponseMessage(e.ResponseReceiverId, aReturn);
        }
        

        // Unwrapps messages from the input channel and forwards them
        // to corresponding output channels.
        private IDuplexChannelUnwrapper myDuplexChannelUnwrapper;

        // Paticular services listening to requests which will be forwarded from
        // the channel unwrapper.
        private IDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData> mySumReceiver;
        private IDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData> mySubtractReceiver;
        private IDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData> myMultiplyReceiver;
        private IDuplexTypedMessageReceiver<CalculatorOutputData, CalculatorInputData> myDivideReceiver;
    }
}

Client


The client is a simple .NET application providing UI to input numbers and choose the type of the calculation. It uses the channel wrapper to send all types of calculation requests via one channel.
When the user hits the button, the message sender sends the request via the local message channel to the channel wrapper. The channel wrapper wraps the message and sends it via TCP duplex output channel to the server application. The server application unwraps the incoming request and forwards it to the correct receiver (service).

.NET client source code:
using System;
using System.Windows.Forms;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.Infrastructure.ConnectionProvider;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.SynchronousMessagingSystem;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
using Eneter.Messaging.Nodes.ChannelWrapper;

namespace CalculatorClient2
{
    public partial class Form1 : Form
    {
        // Input data for calculator requests
        public class CalculatorInputData
        {
            public double Number1 { get; set; }
            public double Number2 { get; set; }
        }

        // Output result from the calculator
        public class CalculatorOutputData
        {
            public double Result { get; set; }
        }

        public Form1()
        {
            InitializeComponent();

            // Internal messaging between message senders and channel wrapper.
            IMessagingSystemFactory anInternalMessaging = new SynchronousMessagingSystemFactory();

            // The service receives messages via one channel (i.e. it listens on one address).
            // The incoming messages are unwrapped on the server side.
            // Therefore the client must use wrapper to send messages via one channel.
            IChannelWrapperFactory aChannelWrapperFactory = new ChannelWrapperFactory();
            myDuplexChannelWrapper = aChannelWrapperFactory.CreateDuplexChannelWrapper();


            // To connect message senders and the wrapper with duplex channels we can use the following helper class.
            IConnectionProviderFactory aConnectionProviderFactory = new ConnectionProviderFactory();
            IConnectionProvider aConnectionProvider = aConnectionProviderFactory.CreateConnectionProvider(anInternalMessaging);

            
            // Factory to create message senders.
            // Sent messages will be serialized in Xml.
            IDuplexTypedMessagesFactory aCommandsFactory = new DuplexTypedMessagesFactory();

            // Sender to sum two numbers.
            mySumSender = aCommandsFactory.CreateDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData>();
            mySumSender.ResponseReceived += OnResultResponse;
            aConnectionProvider.Connect(myDuplexChannelWrapper, mySumSender, "Sum");

            // Sender to subtract two numbers.
            mySubSender = aCommandsFactory.CreateDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData>();
            mySubSender.ResponseReceived += OnResultResponse;
            aConnectionProvider.Connect(myDuplexChannelWrapper, mySubSender, "Sub");

            // Sender to multiply two numbers.
            myMulSender = aCommandsFactory.CreateDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData>();
            myMulSender.ResponseReceived += OnResultResponse;
            aConnectionProvider.Connect(myDuplexChannelWrapper, myMulSender, "Mul");

            // Sender to divide two numbers.
            myDivSender = aCommandsFactory.CreateDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData>();
            myDivSender.ResponseReceived += OnResultResponse;
            aConnectionProvider.Connect(myDuplexChannelWrapper, myDivSender, "Div");

            // We use Tcp for the communication.
            IMessagingSystemFactory aTcpMessagingSystem = new TcpMessagingSystemFactory();

            // Create output channel to send requests to the service.
            IDuplexOutputChannel anOutputChannel = aTcpMessagingSystem.CreateDuplexOutputChannel("tcp://127.0.0.1:8091/");

            // Attach the output channel to the wrapper - so that we are able to send messages
            // and receive response messages.
            // Note: The service has the coresponding unwrapper.
            myDuplexChannelWrapper.AttachDuplexOutputChannel(anOutputChannel);
        }

        private void Form1_FormClosed(object sender, FormClosedEventArgs e)
        {
            // Stop listening by detaching the input channel.
            myDuplexChannelWrapper.DetachDuplexInputChannel();
        }


        private void OnResultResponse(object sender, TypedResponseReceivedEventArgs<CalculatorOutputData> e)
        {
            // If everything is ok then display the result.
            if (e.ReceivingError == null)
            {
                // The response does not come in main UI thread.
                // Therefore we must transfer it to the main UI thread.
                InvokeInUIThread(() => ResultLabel.Text = e.ResponseMessage.Result.ToString() );
            }
        }

        private void CalculateButton_Click(object sender, EventArgs e)
        {
            SendRequestMessage(mySumSender);
        }
         
        private void SubtractButton_Click(object sender, EventArgs e)
        {
            SendRequestMessage(mySubSender);
        }

        private void MultiplyButton_Click(object sender, EventArgs e)
        {
            SendRequestMessage(myMulSender);
        }

        private void DivideButton_Click(object sender, EventArgs e)
        {
            SendRequestMessage(myDivSender);
        }

        private void SendRequestMessage(IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> sender)
        {
            // Prepare input data for the calculator.
            CalculatorInputData anInputForCalculator = new CalculatorInputData();
            anInputForCalculator.Number1 = double.Parse(Number1TextBox.Text);
            anInputForCalculator.Number2 = double.Parse(Number2TextBox.Text);

            // Send the request message.
            sender.SendRequestMessage(anInputForCalculator);
        }

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

        // Wraps requests into one output channel.
        // The service side listens to one address and uses unwrapper to unwrap
        // messages and send them to correct receivers.
        private IDuplexChannelWrapper myDuplexChannelWrapper;

        // Message senders.
        private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> mySumSender;
        private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> mySubSender;
        private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> myMulSender;
        private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> myDivSender;
    }
}


Android client source code:
Please see more details about Android specific settings in my previous article: Android: How to communicate with .NET application via TCP

package calculator.client;

import eneter.messaging.diagnostic.EneterTrace;
import eneter.messaging.diagnostic.EneterTrace.EDetailLevel;
import eneter.messaging.endpoints.typedmessages.*;
import eneter.messaging.messagingsystems.messagingsystembase.*;
import eneter.messaging.messagingsystems.tcpmessagingsystem.TcpMessagingSystemFactory;
import eneter.messaging.messagingsystems.threadmessagingsystem.ThreadMessagingSystemFactory;
import eneter.messaging.nodes.channelwrapper.*;
import eneter.net.system.EventHandler;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;

public class AndroidCalculatorClientActivity extends Activity
{
    // Input data for calculator requests
    public static class CalculatorInputData
    {
        public double Number1;
        public double Number2;
    }

    // Output result from the calculator
    public static class CalculatorOutputData
    {
        public double Result;
    }

    // Communication
    private IDuplexChannelWrapper myDuplexChannelWrapper;
    private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> mySumSender;
    private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> mySubSender;
    private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> myMulSender;
    private IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> myDivSender;
    
    // UI controls
    private Handler myRefresh = new Handler(); 
    private EditText myNumber1EditText;
    private EditText myNumber2EditText;
    private EditText myResultEditText;
    private Button mySumButton;
    private Button mySubButton;
    private Button myMulButton;
    private Button myDivButton;
    
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        
        // Get UI widgets.
        myNumber1EditText = (EditText) findViewById(R.id.number1EditText);
        myNumber2EditText = (EditText) findViewById(R.id.number2EditText);
        myResultEditText = (EditText) findViewById(R.id.resultEditText);
        
        mySumButton = (Button) findViewById(R.id.SumBtn);
        mySumButton.setOnClickListener(myOnSumButtonClickHandler);
        
        mySubButton = (Button) findViewById(R.id.SubBtn);
        mySubButton.setOnClickListener(myOnSubButtonClickHandler);
        
        myMulButton = (Button) findViewById(R.id.MulBtn);
        myMulButton.setOnClickListener(myOnMulButtonClickHandler);
        
        myDivButton = (Button) findViewById(R.id.DivBtn);
        myDivButton.setOnClickListener(myOnDivButtonClickHandler);
        
        openConnection();
    }
    
    @Override
    public void onDestroy()
    {
        // Stop listening to response messages.
        myDuplexChannelWrapper.detachDuplexOutputChannel();
        
        super.onDestroy();
    } 
    
    private void openConnection()
    {
        // In case you need detailed traces from the communication.
        //EneterTrace.setDetailLevel(EDetailLevel.Debug);
        
        // Internal messaging between message senders and channel wrapper.
        IMessagingSystemFactory anInternalMessaging = new ThreadMessagingSystemFactory();
        
        // The service receives messages via one channel (i.e. it listens on one address).
        // The incoming messages are unwrapped on the server side.
        // Therefore the client must use wrapper to send messages via one channel.
        IChannelWrapperFactory aChannelWrapperFactory = new ChannelWrapperFactory();
        myDuplexChannelWrapper = aChannelWrapperFactory.createDuplexChannelWrapper();
        
        // Factory to create message senders.
        // Sent messages will be serialized in Xml.
        IDuplexTypedMessagesFactory aCommandsFactory = new DuplexTypedMessagesFactory();
        
        // Sender to sum two numbers.
        mySumSender = aCommandsFactory.createDuplexTypedMessageSender(CalculatorOutputData.class, CalculatorInputData.class);
        mySumSender.responseReceived().subscribe(myOnResultResponse);

        // Sender to subtract two numbers.
        mySubSender = aCommandsFactory.createDuplexTypedMessageSender(CalculatorOutputData.class, CalculatorInputData.class);
        mySubSender.responseReceived().subscribe(myOnResultResponse);
        
        // Sender to multiply two numbers.
        myMulSender = aCommandsFactory.createDuplexTypedMessageSender(CalculatorOutputData.class, CalculatorInputData.class);
        myMulSender.responseReceived().subscribe(myOnResultResponse);
        
        // Sender to divide two numbers.
        myDivSender = aCommandsFactory.createDuplexTypedMessageSender(CalculatorOutputData.class, CalculatorInputData.class);
        myDivSender.responseReceived().subscribe(myOnResultResponse);

        try
        {
            // Connect senders with the channel wrapper.
            myDuplexChannelWrapper.attachDuplexInputChannel(anInternalMessaging.createDuplexInputChannel("Sum"));
            mySumSender.attachDuplexOutputChannel(anInternalMessaging.createDuplexOutputChannel("Sum"));
            
            myDuplexChannelWrapper.attachDuplexInputChannel(anInternalMessaging.createDuplexInputChannel("Sub"));
            mySubSender.attachDuplexOutputChannel(anInternalMessaging.createDuplexOutputChannel("Sub"));
            
            myDuplexChannelWrapper.attachDuplexInputChannel(anInternalMessaging.createDuplexInputChannel("Mul"));
            myMulSender.attachDuplexOutputChannel(anInternalMessaging.createDuplexOutputChannel("Mul"));
            
            myDuplexChannelWrapper.attachDuplexInputChannel(anInternalMessaging.createDuplexInputChannel("Div"));
            myDivSender.attachDuplexOutputChannel(anInternalMessaging.createDuplexOutputChannel("Div"));
            
            // Create TCP messaging system.
            IMessagingSystemFactory aMessaging = new TcpMessagingSystemFactory();
            
            // Attach the output channel to the channel wrapper and be able to send messages
            // and receive responses.
            myDuplexChannelWrapper.attachDuplexOutputChannel(aMessaging.createDuplexOutputChannel("tcp://10.0.2.2:8091/"));
        }
        catch (Exception err)
        {
            EneterTrace.error("Connecting to the service failed.", err);
        }
    }
    
    private void onSumButtonClick(View v)
    {
        sendRequest(mySumSender);
    }
    
    private void onSubButtonClick(View v)
    {
        sendRequest(mySubSender);
    }
    
    private void onMulButtonClick(View v)
    {
        sendRequest(myMulSender);
    }
    
    private void onDivButtonClick(View v)
    {
        sendRequest(myDivSender);
    }
    
    private void sendRequest(IDuplexTypedMessageSender<CalculatorOutputData, CalculatorInputData> sender)
    {
        CalculatorInputData anInputData = new CalculatorInputData();
        anInputData.Number1 = Double.parseDouble(myNumber1EditText.getText().toString());
        anInputData.Number2 = Double.parseDouble(myNumber2EditText.getText().toString());
        
        try
        {
            sender.sendRequestMessage(anInputData);
        }
        catch (Exception err)
        {
            EneterTrace.error("Sending of calculation request failed.", err);
        }
    }
    
    private void onResponseReceived(Object sender, final TypedResponseReceivedEventArgs<CalculatorOutputData> e)
    {
        // Display the result.
        // Note: Marshal displaying to the correct UI thread.
        myRefresh.post(new Runnable()
            {
                @Override
                public void run()
                {
                    myResultEditText.setText(Double.toString(e.getResponseMessage().Result));
                }
            }); 
    }
    
    
    private OnClickListener myOnSumButtonClickHandler = new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            onSumButtonClick(v);
        }
    }; 
    
    private OnClickListener myOnSubButtonClickHandler = new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            onSubButtonClick(v);
        }
    };
    
    private OnClickListener myOnMulButtonClickHandler = new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            onMulButtonClick(v);
        }
    };
    
    private OnClickListener myOnDivButtonClickHandler = new OnClickListener()
    {
        @Override
        public void onClick(View v)
        {
            onDivButtonClick(v);
        }
    };
    
    private EventHandler<TypedResponseReceivedEventArgs<CalculatorOutputData>> myOnResultResponse = new EventHandler<TypedResponseReceivedEventArgs<CalculatorOutputData>>()
    {
        @Override
        public void onEvent(Object sender, TypedResponseReceivedEventArgs<CalculatorOutputData> e)
        {
            onResponseReceived(sender, e);
        }
    };
}



16 comments:

  1. Hello, we are considering to use .net library in our new project. However when playing with the library we are getting some exceptions which are handles withing the framework... For example the code above is trying to locate an assemlby .XmlSerializers...

    Could not load file or assembly 'ClassLibrary1.XmlSerializers, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

    Frankly this is something our senior SW engineer does not like to see. Is it possible to fix it?

    There are few more thing we have to consider in yout lib in order to get a green for buying the commercial licence. Thanks

    ReplyDelete
    Replies
    1. Hello Jerry, the issue you report looks like the known Microsoft problem with XmlSerializer class that is also internally used by XmlStringSerializer from Eneter.

      From MSDN:
      The serializer examines all public fields and properties of the Type to learn about which types an instance references at runtime. It then proceeds to create C# code for a set of classes to handle serialization and deserialization using the classes in the System.CodeDOM namespace. During this process, the XmlSerializer checks the reflected type for XML serialization attributes to customize the created classes to the XML format definition. These classes are then compiled into a temporary assembly and called by the Serialize() and Deserialize() methods to perform the XML to object conversions.

      ... the assembly is not present because the compilation failed, which may happen because, under rare circumstances, the serialization attributes produce code that the C# compiler fails to compile.

      Here is the link to MSDN:
      http://msdn.microsoft.com/en-us/library/aa302290.aspx
      http://msdn.microsoft.com/en-us/library/aa302290.aspx#trblshtxsd_topic6

      This problem is also discussed on stackoverflow:
      http://stackoverflow.com/questions/1127431/xmlserializer-giving-filenotfoundexception-at-constructor


      If you wish you can send me the data stracture you try to serialize and I can investigate it in more detail.

      Delete
  2. Is it also possible to publish the same set of services via different protocols / ports. If yes, do you have an example for it?

    ReplyDelete
    Replies
    1. Hi Daniel, if I understand you correctly then yes.
      Here is the example showing a services exposing its services via multiple protocols. (you can modify the example to put there as many protocols and ports you want)

      http://eneter.blogspot.de/2011/05/service-listening-to-tcp-http-and-named.html

      The example was also on code project from where you can also download the example:
      http://www.codeproject.com/Articles/196551/Service-Listening-to-TCP-HTTP-and-Named-Pipe-at-th

      Delete
    2. Well it's not exactly what I mean. What I mean is a combination of Dispatcher and Channal Wrapper/Unwrapper, so I can implement mutliple services that are published together via 1 or more protocols. For example would it be possible to extend the example in this blog post with an HTTP connection and Shared Memory?

      Delete
    3. Ok, I think I understand what you mean.
      I would like to have this example but listening via multiple protocols.

      It is easily possible. You just need to put the dispatcher before the duplex channel unwrapper (on the server side) and connect them via a local channel.

      I have modified the example from this blog to listen via SharedMemory and WebSockets. (If it is possible for you consider to use websockets instead of http.)

      You can download the example from here:
      http://eneter.net/Downloads/Examples/Calculator2OneChannelServiceMulProtocols.zip

      Delete
    4. Thanks, that's exactly what I was looking for.

      Delete
  3. Thank you for the explanation! I looked for this information hours and I tried to do a lot of things but nothing worked. On your blog I can always find what I need. Thank you so much !

    ReplyDelete
  4. hi
    first thank you so much and I'm not Professional programmer
    so please explain simple

    I have two question

    1.is this code work for listen multiple client at same time (for example two client want connect at same moment)
    if yes how many and how config the number?
    if no how can do it?

    1.how can set client and server do one work together
    i have data base in server and string in client
    i want save string in data base and return true to client if save it
    the main problem is if internet lost between send back true
    the data base save it
    but client think not save because string true lost
    how can handle it?

    ReplyDelete
    Replies
    1. i want string true if dont get by client
      the server remove string from data base

      Delete
    2. Hi Mohamad,
      To your first question:
      Yes, the service can be connected as many clients as you want.

      To your second question:
      If I understand correctly your client needs a confirmation the string was stored in DB.
      I would handle it the following way. If the confirmation "true" string does not come within a time you specify the client can send another message that would explicitly ask the service if the string is saved. The service would then return true if the string is saved or false if not.

      I hope my response will help.

      Delete
  5. tnk ondrej

    and another qus:D


    how can send and receive data between client and server when i have only server ip?
    the above example and other example need to have ip of two side
    and i have only ip of server

    if you have and example please get me link
    tank you

    ReplyDelete
    Replies
    1. Hi Mohamad,

      It is ok. To open the connection you need only server IP. It is also shown in the example from this article. You will do it like this:

      E.g. if the IP address is: 89.45.23.55 then

      On the server side you will create the input channel:
      IDuplexInputChannel anInputChannel = aServiceMessagingSystem.CreateDuplexInputChannel("tcp://89.45.23.55:8091/");

      Then on the client side you will create the output channel:
      IDuplexOutputChannel anOutputChannel = aTcpMessagingSystem.CreateDuplexOutputChannel("tcp://89.45.23.55:8091/");

      The logic is that the service starts listening on an IP address and port and the client opens connection to that IP address and port.

      Delete
  6. hi
    how can solve this:

    1)server is off
    app open and try connect to server and cant connect
    then server turn on and in app try reconect
    app dont connect until app close and open again

    how can have reconnect button in app that dont need to cose the app

    2)how and if conection lost to the server show in app(like set text box "coonection lost")

    ReplyDelete
    Replies
    1. yes, it is possible to reconnect without the restart. I can create a button and then if you use the example above you can have code like this for handling the click on the button:

      myDuplexChannelWrapper.AttachedDuplexOutputChannel.OpenConnection();

      You also can detect if the connection is closed. You can subscribe the following event:

      myDuplexChannelWrapper.ConnectionClosed += OnConnectionClosed;

      Delete