The code is available on GitHub here
Interprocess communication (IPC) is a mechanism used by operating systems to enable communication and data exchange between different processes. In this article, we will explore various IPC techniques and their implementation using a core IPC communication system.
Different IPC approaches
There are several techniques used for interprocess communication, including:
- NamedPipe – A pipe is a unidirectional communication channel that allows the transfer of data between two processes. One process writes data to the pipe, and the other process reads the data. Check Interprocess communication – Named Pipes in .NET C# for a NamedPipes implementation.
- File – a file/record on disk or a file server can be accessed by multiple processes.
- Shared Memory – Shared memory allows two or more processes to share a region of memory, allowing them to exchange data more efficiently.
- Message Queues – Message queues allow processes to send messages to each other using a queue data structure. One process can add a message to the queue, and another process can retrieve the message from the queue.
- Sockets – Sockets are used for communication between processes running on different machines. A socket is a combination of an IP address and a port number that identifies a particular process on a particular machine.
- Remote Procedure Calls (RPC) – RPC is a protocol that allows a client process to call a procedure on a server process, as if the procedure were a local function call.
A Core IPC Communication implementation
To design a core system that meets the requirements of various implementations, we will use the proper abstract classes and interfaces that model the core aspects of an IPC system.
We will create an IPCConnection interface that allows us to send and receive messages and be notified when the other end is disconnected. Both the client and server will inherit from this interface. Additionally, we will use the EventHandler class and events of the .NET framework to ensure that our implementations are fully asynchronous and non-blocking to the component using them.
Here are the interfaces we will use:
public interface IPCConnection : IDisposable
{
Task Send(string message);
event EventHandler Disconnected;
event EventHandler<MessageReceivedEventArgs> MessageReceived;
}
public interface IServer : IPCConnection
{
Task Start();
event EventHandler ServerStarted;
event EventHandler ClientConnected;
}
public interface IClient : IPCConnection
{
Task Connect();
event EventHandler ConnectedToServer;
event EventHandler ClientStarted;
}
public class MessageReceivedEventArgs : EventArgs
{
public string Message { get; }
public MessageReceivedEventArgs(string message)
{
Message = message;
}
}
The code above shows the implementation of the IPCConnection interface, as well as the IServer and IClient interfaces that inherit from it. The MessageReceivedEventArgs class is used to pass the received message between processes.
Plug in Different implementations
To plug in a new implementation, we simply need to extend the IServer and IClient interfaces appropriately. The core components (IServer and IClient) exist in the core layer of the architecture, while the various implementations reside in the outer layer, making them higher-level components.
In the Interprocess Communication Hexagonal architecture, the core components (IServer, IClient, and the rest of the core components) exist in the center, while the plugins/higher-level components reside in the outer layers.
For a NamedPipes implementation you can visit the Interprocess communication – Named Pipes in .NET C#.