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 the 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 the life and makes the startup error prone.
The example bellow 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 2.0 that provides components for various communication scenarios.
(Full, not limited and for non-commercial usage free version of the framework 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 { // The message receiver. // It receives 'string' and responses 'DateTime'. // Note: the 'string' is not used in this example. static IDuplexTypedMessageReceiver<DateTime, string> myReceiver; static void Main(string[] args) { // Create TCP based messaging. IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory(); IDuplexInputChannel anInputChannel = aTcpMessaging.CreateDuplexInputChannel("tcp://127.0.0.1:7083/"); // Create typed message receiver. IDuplexTypedMessagesFactory aTypedMessagesFactory = new DuplexTypedMessagesFactory(); myReceiver = aTypedMessagesFactory.CreateDuplexTypedMessageReceiver<DateTime, string>(); myReceiver.MessageReceived += OnMessageReceived; // Attach the receiver to the channel and start listening. 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) { // Process the incoming request: // Just return the current time. 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 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:
1. Start the client application.
2. Invoke the request to get the exact time.
3. Wait few seconds and start the service application.
4. 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 { // The message sender. // It sends 'string' and gets 'DateTime' as the response. // Note: the 'string' is not used in this example. IDuplexTypedMessageSender<DateTime, string> mySender; public Form1() { InitializeComponent(); // Create TCP based messaging. IMessagingSystemFactory aTcpMessaging = new TcpMessagingSystemFactory(); // Create the buffered messaging. // If the connection is not available, the buffered messaging tries to reconnect // and meanwhile stores sent messages to the buffer. // Then, when reconnected, it sends sent messages to the receiver. // The following buffered messaging is based on TCP messaging // and can work offline 1 minute. IMessagingSystemFactory aBufferedMessaging = new BufferedMessagingFactory(aTcpMessaging, TimeSpan.FromMinutes(1)); IDuplexOutputChannel anOutputChannel = aBufferedMessaging.CreateDuplexOutputChannel("tcp://127.0.0.1:7083/"); // Create the message sender. IDuplexTypedMessagesFactory aTypedMessagesFactory = new DuplexTypedMessagesFactory(); mySender = aTypedMessagesFactory.CreateDuplexTypedMessageSender<DateTime, string>(); mySender.ResponseReceived += OnResponseReceived; // Attach the output channel and be able to send requests and receive // response messages. mySender.AttachDuplexOutputChannel(anOutputChannel); } private void Form1_FormClosed(object sender, FormClosedEventArgs e) { // Correctly close the connection. mySender.DetachDuplexOutputChannel(); } private void RequestServiceTimeButton_Click(object sender, EventArgs e) { // Invoke the request to get the current time from the service. mySender.SendRequestMessage("not used"); } private void OnResponseReceived(object sender, TypedResponseReceivedEventArgs<DateTime> e) { if (e.ReceivingError == null) { // Display the time returned from the service. // Note: The response does not come in the UI thread, therefore // it must be transfered to UI thread. DateTime aTime = e.ResponseMessage; InvokeInUIThread(() => ServiceTimeTextBox.Text = aTime.ToString()); } } // 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