Summary: A simple example showing how to implement the communication where the client application can start to communicate before the service application is running.
Introduction
Typically, the startup order matters in the interprocess communication. It means, the client application assumes that the service application is running and is ready to communicate. This behavior does not have to be a problem for systems, where the service is running most of the time and clients start and stop regularly.
But for systems consisting of more applications running on separate machines and starting approximately at the same time, this behavior causes that somebody (or something) must start the system in a defined order, what complicates life and makes the startup error prone.
The example below implements a simple client-service communication where communicating applications can start independently from each other and the client can start to communicate even before the service is able to receive messages.
The example is based on the Eneter Messaging Framework that provides components for various communication scenarios.
(The framework is free and can be downloaded from http://www.eneter.net. The online help for developers can be found at http://www.eneter.net/OnlineHelp/EneterMessagingFramework/Index.html.)
Buffered Messaging
To implement the independent startup order behavior, we will use Buffered Messaging from the Eneter Messaging Framework.
In case the receiving application is not connected, the Buffered Messaging automatically tries to open the connection and meanwhile stores sent messages into the buffer. Then, when the connection is open, it sends the stored messages to the receiver.
In our independent startup order scenario, if the client starts before the service and sends some message, the message is stored in the buffer. Then, when the service is started and the connection is open, the message from the buffer is sent to the service.
Service Application
The service application is a simple console application communicating via TCP and returning the time. The whole implementation is very simple.
using System;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
namespace TimeService
{
class Program
{
static IDuplexTypedMessageReceiver<DateTime, string> myReceiver;
static void Main(string[] args)
{
IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory();
IDuplexInputChannel anInputChannel =
aTcpMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:7083/");
IDuplexTypedMessagesFactory aTypedMessagesFactory =
new DuplexTypedMessagesFactory();
myReceiver = aTypedMessagesFactory.CreateDuplexTypedMessageReceiver
<DateTime, string>();
myReceiver.MessageReceived += OnMessageReceived;
myReceiver.AttachDuplexInputChannel(anInputChannel);
Console.WriteLine("The service is listening.");
Console.WriteLine("Press Enter to stop.");
Console.ReadLine();
myReceiver.DetachDuplexInputChannel();
}
static void OnMessageReceived(object sender,
TypedRequestReceivedEventArgs<string> e)
{
myReceiver.SendResponseMessage(e.ResponseReceiverId, DateTime.Now);
}
}
}
Client Application
The client application is a simple application using the service to retrieve the exact time.
The client application can start and can request the time before the service application is running.
In such a case, the request is stored in the buffer. Then, when the service is ready, the request is delivered and the service responses the time.
Try the following scenario:
- Start the client application.
- Invoke the request to get the exact time.
- Wait few seconds and start the service application.
- The client displays the time from the service.
The implementation is very simple.
using System;
using System.Windows.Forms;
using Eneter.Messaging.EndPoints.TypedMessages;
using Eneter.Messaging.MessagingSystems.Composites.BufferedMessagingComposit;
using Eneter.Messaging.MessagingSystems.MessagingSystemBase;
using Eneter.Messaging.MessagingSystems.TcpMessagingSystem;
namespace TimeClient
{
public partial class Form1 : Form
{
IDuplexTypedMessageSender<DateTime, string> mySender;
public Form1()
{
InitializeComponent();
IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory();
IMessagingSystemFactory aBufferedMessaging =
new BufferedMessagingFactory(aTcpMessaging, TimeSpan.FromMinutes(1));
IDuplexOutputChannel anOutputChannel =
aBufferedMessaging.CreateDuplexOutputChannel("tcp://127.0.0.1:7083/");
IDuplexTypedMessagesFactory aTypedMessagesFactory =
new DuplexTypedMessagesFactory();
mySender = aTypedMessagesFactory.CreateDuplexTypedMessageSender
<DateTime, string>();
mySender.ResponseReceived += OnResponseReceived;
mySender.AttachDuplexOutputChannel(anOutputChannel);
}
private void Form1_FormClosed(object sender, FormClosedEventArgs e)
{
mySender.DetachDuplexOutputChannel();
}
private void RequestServiceTimeButton_Click(object sender, EventArgs e)
{
mySender.SendRequestMessage("not used");
}
private void OnResponseReceived(object sender,
TypedResponseReceivedEventArgs<DateTime> e)
{
if (e.ReceivingError == null)
{
DateTime aTime = e.ResponseMessage;
InvokeInUIThread(() => ServiceTimeTextBox.Text = aTime.ToString());
}
}
private void InvokeInUIThread(Action action)
{
if (InvokeRequired)
{
Invoke(action);
}
else
{
action();
}
}
}
}
And here are communicating applications: