Inter-process communications


WCF is a pretty comprehensive communications framework but is daunting to use. Even for practitioners experienced in the art there is lots do each time a new project is started. Lots of options, lots of configuration most of which is daunting to the uninitiated.

I’ve used WCF in the past to create web applications independent of IIS so I am able to appreciate the functionality available – and the complexity involved using WCF. I have a situation that occurs regularly that requires inter-process communications in managed applications. WCF is great for this because it allows a developer to create endpoints using, say, named pipes or http and send SOAP messages between the two to effect commands and responses. But there’s lot of boilerplate code to copy each time to allow two processes to find each other, communicate, define contracts, ensure the other process is alive, handle errors and faults, etc.. So this project focuses on one scenario and provides a collection of classes to implement a framework such that a programmer need only provide 2 interface classes defining the service contracts and two classes implementing those classes. Each of these classes inherit from a framework class and need only add a few lines of code in a formulaic way to create a pair of executables that are able to communicate. And no WFC configuration (or knowledge of it) is required.

License

This post and the code available here is provided under CPOL permissive license.
Download source code. This code is configured to use .NET 3.5.

Scenario

The scenario is an application which needs to communicate with (or spawn and) communicate with another process. There are several reason I need such a facility. For example to be able to create debug clients for services, to be able to execute some functions with different credentials when impersonation in one application is not an acceptable option. So this is the scenario and the one for which the framework has been created but adapting it for use in other scenarios is straight forward (though some knowledge of WCF may be required). A tiny amount of WCF knowledge is anyway helpful because the application service contract interfaces must be tagged with WCF attributes so the underlying WCF is able to generate SOAP documents automatically. However the simple application included in the code illustrates the small range of tags attributes and examples will appear later.

The sequence diagram below gives some idea of the classes involved and their interactions. Those classes highlighted with a green box are the one you need to implement. Almost exclusively, these classes implement the methods defined by the service contract interfaces which is your code, the code that implements your application’s functionality. The only unusual feature is the way you call methods from the requestor to the processor or from the processor to the requestor (and more of this later). The classes without a highlight box are either from this framework or from WCF.

‘Requestor’ and ‘Processor’? You can think of a requestor as being a client and the processor a server but the terms ‘client’ and ‘server’ carry a lot of specific expectation. For example the server will already be running, it probably doesn’t initiate contact with a client, etc.. So to avoid problems with existing expectations the terms client and server are not used. However in this framework the requestor is the code that will be in your application and the processor will be some other code, probably in a different executable.

As you can see in the diagram, the framework implements a GenericRequestor and a GenericProcessor. This pair of classes abstract all the WCF configuration and setup and manage the interaction between the requestor and processor. The concrete classes (your implementations) extend these classes and inherit this functionality. The third class, the ConcreteController is an optional class you may choose to implement. However equally you might choose to use the GenericRequestorController of the framework directly. Again, more of this later.

Getting started

In this section of the post I’m going to walk through using the framework to create a requestor/processor pair. The code associated with this post contains all the stuff described below so while you can follow along, there’s no need.

Interfaces

The start point are the interfaces which define the methods the requestor will be able to call on the processor and which the processor will be able to call on the requestor. In the framework, there are two interfaces IRequestToProcessor and IProcessorToRequest. These are implemented by GenericRequestor and GenericProcessor respectively. In fact if you look carefully, you will see that both classes contain methods of both interfaces though they only implement one interface. This is because the code in the method is different. For example, Register() is implemented by both the GenericRequestor and GenericProcessor. However the implementation in the processor simply marshals the call over a communications channel passing arguments while the requestor implementation does something with those arguments. In this case return an acknowledgement so the process knows to enter a waiting loop by calling the private method BeginWaiting().

So the first thing to do when creating your own implementation is create a class library project in which your requestor and processor interfaces will be created. You will want to add a reference to the .NET System.ServiceModel assembly to this project. In the example supplied, this project is called ConcreteInterfaces which generates an assembly called Lyquidity. ConcreteInterfaces that is also the name of the default namespace.

Here’s the definition of the IConcreteRequestorToProcessor interface in this assembly (minus the comments):

[ServiceContract(SessionMode = SessionMode.Allowed)]
public interface IConcreteRequestorToProcessor
{
	[OperationContract(IsOneWay = true)]
	void MyRequestorAction1();

	[OperationContract]
	bool MyRequestorAction2(string something);

	[OperationContract]
	bool MyAsynchAction(object somedata);
}

This is a standard interface with some WCF attributes thrown in. The class is marked as being a ServiceContract. This is boiler plat stuff and lets WCF know which interfaces it needs to implement. The interfaces you create should copy this slavishly.

Each of the interface methods are also marked as being an OperationContract. If the method is void then the OperationContract attribute can also specify IsOneWay=true. This additional step is not necessary but it does allow WCF to know it will not need to marshal a response back to the caller. Note that if you specify IsOneWay=true on a signature that does have a return value you will see an error when the service host tries to begin listening complaining of a mismatch.

The example, interfaces are in a separate assembly so the same interfaces can be referenced from the requestor and processor projects. You may already have a project which meets this requirement.

Bear in mind that not every object can be passed via an interface call. When deciding whether to include a class as a parameter or a return value you should consider whether it is possible to serialize the object. That is, is there an Xml serializer for the class. If there is, the class can be used you are OK. If one does not exist you will either have to create one or find another way to pass the data in the class. For example, it’s not possible to marshal exceptions this way. Some exceptions contain elements that are both private and not serialized. (Note I am referring to expected exceptions occurring in the processor code that you might like to know about in the requestor. I am not referring to SOAP faults.)

The Requestor

The requestor project in the example is a Console Application however in your version the requestor may be implemented in an existing assembly. In our case the requestors are implemented in separate assemblies but Class Library assemblies. In the example, the requestor project generates an assembly called Lyquidity.ConcreteRequestor that is also the name of the default namespace. Again you will need to reference the System.ServiceModel assembly from the .NET framework. In addition you will also need to add a reference to the GenericProcessorRequestor assembly of the framework and add a reference to the assembly containing your interfaces.

In the example, the requestor class is called ConcreteRequestor. It extends GenericRequestor and implements the two interfaces:

[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant, InstanceContextMode = InstanceContextMode.Single)]
public class ConcreteRequestor : GenericRequestor, IConcreteProcessorToRequestor, IConcreteRequestorToProcessor

Class is decorated with a ServiceBehavior attribute which boilerplate stuff that you can copy and paste into your own requestor implementations. This attribute tell WCF this class is to host a WCF ServiceHost which provides the listening and other communication facilities. The attribute parameters also tells WCF this class contains thread-safe code so it can be used by multiple instances simultaneously and there will just one instance of the requestor listener that will service all communications with processors.

The class needs a constructor but it does nothing except pass along the address to use:

public ConcreteRequestor(string requestorAddress) : base(requestorAddress)
{}

The constructor itself does nothing. However the call to the base constructor ensures this framework is able to configure WCF by, for example, creating the ServiceHost instance that will arrange communications with a processor and register the IProcessorToRequestor interface with the ServiceHost instance so the host is able to expect and route in-bound and out-bound calls. It also passes a unique address of your choosing to the GenericRequestor. This address is used by the ServiceHost as the name it will respond to. I write ‘of your choosing’ but it must conform to the named pipes naming convention with really means it must start ‘net.pipe://’.

The constructor also call the virtual method Initialize() which you can override to register an requestor interface if you have one. This method is the only other place where anything related to setup up the communications exists:

protected override Initialize()
{
	base.AddServiceEndpoint(typeof(IConcreteProcessorToRequestor), RequestorAddress);
}

If your requestor does not implement any additional methods for the processor to call (that’s additional to the ones exposed by the base GenericRequestor) then you do not need to implement this override.

Note: The examples use named pipes for communications but the framework will accommodate named pipe, http or net.tcp communication channels. The framework will create a protocol specific channel binding based on the protocol used in the requestorAddress you supply.

The remaining code in the ConcreteRequestor implements the methods the requestor will call on the processor and those the processor will call on the requestor. The code for the latter is your code. You know what you want to do. Calling methods exposed by the process is almost as straight forward as somehow typing:

processor.MyRequestAction()

but not quite. WCF works via the operation contracts defined in the two interfaces you defined. The call to the processor is made over a WCF ‘channel’ created by the GenericRequestor which implements proxies for the methods described by your interfaces. So instead of typing the line above the incantation to call a method on the processor is:

ExecuteAction<IConcreteRequestorToProcessor>((processor, n) =>
{
	processor.MyRequestorAction1();
	return true;
}, "MyRequestorAction1");

The base method ExecuteAction<> is generic and is responsible for creating a channel of the interface type requested which is a proxy for the processor. The interface used must be one that you have created, that is decorated with appropriate WCF attributes (see above) and is registered as an interface of the processor’s listener (see below). The ExecuteAction<> method takes a delegate argument which is called when the channel has been created. The delegate must not return the value of the call to the process (which anyway might not be a Boolean value). The return value indicate whether the call was successful or not. The second parameter is a string that will appear in log message recording any problems executing the call. I’ve used the name of the method. You might choose something else.

This pattern is then copied and used in each of the interface method implementations.

So calling a method on the processor is not one line but nor is it difficult. The one line call could be realized by implementing another class called, say, processor and implementing calls to ExecuteAction<>() in the methods of that class. However that just hiding the same amount of work so doesn’t really offer much of a benefit in my view.

ExecuteAsychAction<>() is synchronous meaning it will block until the call to the process returns. If you would like calls to be asynchronous you can use ExecuteAsychAction<>(). After using this method you are able continue processing on the requestor and continue until the requestor’s IsFinished returns true. Another way to be notified that an asynchronous method has completed is to create a ‘Finished’ delegate before the call to ExecuteAsychAction<>(). This will be called when the processor return control. At this time you are able to check for errors and/or any data returned (the processor will return data by calling the TransferData() method).

req.Finished = (requestor) =>
{
	if (requestor.IsErrored)
		this.Error = requestor.Error;

	// Do something, perhaps using requestor.Data;
};

ExecuteAsychAction<IMyInterface>((processor,n) { processor.MyLongRunnningMethod(); return true; });

This pattern is similar to the asynchronous Begin…()/End…() pattern used across the .NET framework (at least until 4.5 and ‘asynch’ keyword).

The processor

Creating a processor is very similar to creating a requestor as all the techniques are the same. The processor project in the example is a Console Application. The processor project generates an assembly called Lyquidity.ConcreteProcessor which is also the name of the default namespace. Again you will need to reference the System.ServiceModel assembly from the .NET framework. In addition you will also need to add a reference to the GenericRequestProcessor assembly of the framework and add a reference to the assembly containing your interfaces.

The class constructor will follows the same pattern as the requestor constructor: it does little but pass on arguments. Again, the virtual Intialize() method can be overridden to register the interface which defines the methods the requestor is able to use by calling base.AddServiceEndpoint(). And again the rest of the code implements the interface methods for both the IConcreteProcessorToRequestor and IConcreteRequestorToProcessor interfaces.

The Main(string[] args) method of the application will process the args to ensure the requestor address and the unique processor id is passed from the requestor. The method then creates the ConcreteProcessor and calls the Start() method. The start method is implemented by the GenericProcessor and begins a loop that blocks until a Terminate() call is made by the requestor. The loop also sends a Heartbeat message each second so a requestor is able to determine if the processor is alive. The requestor will end (throw an exception and kill the processor) during asynchronous calls if 10 consecutive heartbeats are missed.

Controller

Implementing your own controller is optional but if you do not use your own, use the GenericController. Calling the Start() method of the generic controller will do a couple of things: start the processor passing the requestor address and the processor id; and wait for the processor to call the register method and handle the event that the register method is not called within 5 seconds.

This might be the way the requestor is controlled:

var requestor = new ConcreteRequestor("net.pipe://localhost/ConcreteRequestor"), "Lyquidity.ConcreteProcessor.exe")
using (var controller = new GenericRequestController(requestor)
{
	if (controller.Requestor as ConcreteRequestor == null) throw new Exception("Oops");

	if (controller.StartInTheCurrentFolder())
	{
		// Do something
		(controller.Requestor as ConcreteRequestor).MyRequestorAction1();
	}
	else throw new Exception(string.Format("Oops 2: ", controller.Error));
}

Or create your own controller to package the requestor address and processor executable so they cannot be affected:

using (var controller = new ConcreteRequestController())
{
	if (controller.Requestor == null) throw new Exception("Oops");

	if (controller.StartInTheCurrentFolder())
	{
		controller.Requestor.MyRequestorAction1();
	}
	else throw new Exception(string.Format("Oops 2: ", controller.Error));
}

Or use the method that starts the requestor and performs your actions in its own thread using the GenericRequestController or your version:

var requestor = new ConcreteRequestor("net.pipe://localhost/ConcreteRequestor"), "Lyquidity.ConcreteProcessor.exe")
using (var controller = new GenericRequestController(requestor)
{
	if (controller.Requestor as ConcreteRequestor == null) throw new Exception("Oops");

	if (!controller.StartInThreadInTheCurrentFolder
	(
		() =>
		{
			if ((controller.Requestor as ConcreteRequestor).MyRequestorAction2("XX"))
				(controller.Requestor as ConcreteRequestor).MyRequestorAction1();

			return true;
		})
	)
	{
		Console.WriteLine("Error: {0}", controller.Error);
	}
}

In this last example, the code to be executed by the requestor is contain in a delegate function passed to the StartInThreadInTheCurrentFolder() method. This is not the only way to achieve this effect but it is a built-in option.

Dispose pattern

All the framework classes implement the dispose pattern so they clean up when they go out of scope. When a Requestor goes out of scope, the ServiceHost is closed. When a Processor goes out of scope it closes the ServiceHost and terminates the executable. When the GenericRequestController goes out of scope the Requestor is disposed , the processor is terminated and then the process is killed.

Handling large files

Under the covers WCF uses SOAP (Xml) messages to pass information between end points. The default Xml serialization configuration used by WCF permits a maximum entity size of 2^15 bytes. This is god enough for many applications however if, like mine, your application requires the ability to pass file contents then the default limit is insufficient. So the WCF channels created by the this framework changes the default maximum size to int.MaxValue.

Mono

The framework will work under Mono though there are some limitations and quirks because of the current state of the Mono WCF implementation.

Use 2.8 or later however the 4.0 implementation cannot be used even in 3.0.2 because it is not sufficiently complete.

Named pipes cannot be used as the named pipes functionality is not sufficiently complete.

When using Http the processor and requestor must use separate ports. Under Windows only the full end point must be unique however under Mono the port must be unique even if the end point is otherwise unique.

A service host end point cannot use port 80. Well, it can use it but any attempt to access the end point will fail and the caller will report that the server actively refused the connection.

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

Be the first to leave a comment!