Interprocess Communication: Part 1

November 20, 2014

Blog | Development | Interprocess Communication: Part 1
Interprocess Communication: Part 1

Interprocess Communication (IPC) is seldom talked about, but drives some of the most powerful software in the world. So, what is IPC exactly? It’s a realm of software engineering dedicated to allowing processes (not necessarily those running on the same physical machine) a reliable mechanism for communicating with each other. If, for example, you and @GeekHive have a great conversation on Twitter, the Twitter platform is, in a sense, the mechanism by which the communication occurs. You say something, we reply… to Twitter, they’re just 1s and 0s, but to both of us, they are a meaningful exchange.


So what is a process? A process is compiled application code running on a computing system, Notepad, Microsoft Word, Google Chrome – are all examples of processes. If you look at the Windows Task Manager, you can clearly see them having a party.


Task Manager




In the next screen shot, we can see that I have a few instances of Visual Studio running, as well as a couple of console windows, and… wait… Why are there so many instances of Chrome running?


Task Manager Chrome




Oh my. Google, what have you done?!



Before we grab our pitchforks and head to Mountain View to demand answers, Google has already addressed this here. The short version? It’s by design.



Before we get too much deeper, let’s look at how modern operating systems manage processes. Your OS does a great job of allowing processes to flip-flop between processors, as well as queuing up different processes to a distinct processor to maximize the throughput of the system as a whole. If a single process is developed as multi-threaded application, the OS can actually allocate the process to run each thread on a different processor.


But what does the operating system do when a single process, implemented as a single thread, is running a highly processor-intensive piece of logic? Well, it sits and watches, like the waterboy at a foosball game.


Even if your high-powered gaming rig is sitting with 8 processors in its arsenal, that single process can, and always will, run on only one of those 8, leaving you with 7 heavy chunks of metal burning electricity with nothing to show for it.


Keeping this in mind, let’s get back to Google Chrome and what the Chromium Blog refers to as a Multi-Processor Architecture. The minds at Google decided that a single process wasn’t enough and instead opted to have a distinct process for each piece of the browser. That means one for rendering HTML, one for every plugin (e.g. Flash), one for just about every tab you have opened, etc.


Was it a home run? That’s hard to say, but some people seem to think it’s the right way forward.


How exactly does one get away with having multiple processes that don’t step on each other’s toes?



Welcome to the gates of IPC! Leave your bags and worries at the door and grab a margarita! Sit back, relax, and enjoy your stay.





Depending on your background, some of the methods for interprocess communication might seem obvious while others will be terms you’ve never heard before.





File 


In general, file-based communication can be considered the simplest form of IPC. ProcessA can write a message into a file, CommFile.txt, and ProcessB can check the value from the file. Minor complications can arise, such as ProcessB should only check the value in the file after ProcessA has actually deposited a message, but we won’t get into that right now.



Socket

Sockets are primarily used to transmit data across network boundaries, between two systems. However, sockets are simply a communication mechanism that relies on the network interface – technically, a system could open an outgoing socket to itself and transmit data in that way – between two processes. There would be overhead involved in the transmission as a result of having to package the data for network transmission, but in some cases that may be an acceptable price to pay.





Signal


Signals are a low level, predefined value sent between processes by the OS and not traditionally used by client applications. A great example of a signal that you may have heard of is the “9” in the “kill -9” command. Kill is a command used to terminate a running process, and the “-9” argument indicates the signal to be sent along with the command, in this case the SIGKILL, a digital equivalent to a molotov cocktail that results in an unstoppable kill action being performed on the process.


Does that mean signals are only used for killing processes? Absolutely not! Based on the full list of POSIX signals, values range from error notifications, child notification, continuation signals, etc. A few custom placeholders exist (SIGUSR1, SIGUSR2) for use-case specific support, and theoretically, any byte value beyond these standards can be used.


Embedded programs may find signals useful for interprocess communications, as the computational and memory footprint for signals is small. – Wikipedia: Unix Signal





Let’s see it in action!



The full source for the following examples can be downloaded here. Note: signal communication has been omitted as a result of C#, our language of choice, having abstracted the bulk of the concepts relating to process signals out of the language. Also, it’s likely still possible to consume process signals using the Platform Invoke services. You can find more about this feature on the Interoperability pages of MSDN, which is geared towards leveraging existing unmanaged application code within a managed architecture.





Files


The code sample below shows how data can be transmitted using a file between two actively running processes. This example uses a simple polling mechanism, checking for the existence of the file prior to reading, and purging the contents once it is read. A more robust solution that depends on file-based communication should replace polling with a notification, such as the one that the FileSystemWatcher class provides.


private const string SharedFileName = "sharedfile.txt";

public void RunReader()
{
    bool doLoop = true;
    do
    {
        if (File.Exists(SharedFileName))
        {
            string line;
            using (var stream = new StreamReader(SharedFileName))
            {
                line = stream.ReadLine();
            }
            Console.WriteLine("From File: " + line);
            File.Delete(SharedFileName);
            doLoop = (line != "exit");
        }
        else
        {
            Thread.Sleep(1000);
        }
    } while (doLoop);
}

public void RunWriter()
{
    bool doLoop;
    do
    {
        Console.Write("Enter a value to write ("exit" to quit): ");
        var line = Console.ReadLine();
        using (var stream = new StreamWriter(SharedFileName, false))
        {
            stream.WriteLine(line);
        }
        doLoop = (line != "exit");
    } while (doLoop);
}


Sockets

Sockets are primarily used for inter-network communication because they operate at the network layer of a system and are bound to a specific IP address and port. Luckily, there’s no place like 127.0.0.1, and we can leverage the 127.0.0.1 loopback IP address to allow a process to transmit to itself.

Socket communication is treated as Internet traffic for the most part, and any firewalls or port restrictions must be adhered to. Thankfully, Windows Firewall has no problem allowing internal traffic unfettered communication access, and may also be configured to ease restrictions on local traffic as well.

The key requirement with socket communication is that the tech world communicates in bits and bytes. This means that any data transmitted over the socket must be first deconstructed into bytes, then reconstituted on the other side in the reverse fashion. Dealing with sockets directly is likely reserved to performance-critical systems, embedded application development, or perhaps a small homebrew application where having every detail fine-tuned for your specific use-cases is truly desirable. In all other cases, a framework such as the Windows Communication Foundation framework, can (and should) be leveraged. 

private const int port = 65534;

public void RunReader()
{
    using (var listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
    {
        var localEndPoint = new IPEndPoint(new IPAddress(new byte[] { 127, 0, 0, 1 }), port);
        listener.Bind(localEndPoint);
        listener.Listen(1);

        var doLoop = true;
        do
        {
            Console.WriteLine("Waiting for a connection...");

            var handler = listener.Accept();

            while (doLoop)
            {
                var incomingBuffer = new byte[1024];
                var bytesRec = handler.Receive(incomingBuffer);
                var data = Encoding.ASCII.GetString(incomingBuffer, 0, bytesRec);

                Console.WriteLine("Message Received : {0}", data);
                if (data == "exit")
                {
                    handler.Shutdown(SocketShutdown.Both);
                    handler.Close();
                    doLoop = false;
                }
            }
        } while (doLoop);

        listener.Close();
    }
}

public void RunWriter()
{
    Console.WriteLine("Running Socket Writer...");

    using (var sender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp))
    {
        var remoteEndPoint = new IPEndPoint(new IPAddress(new byte[] { 127, 0, 0, 1 }), port);
        try
        {
            sender.Connect(remoteEndPoint);
        }
        catch
        {
            Console.WriteLine("Failed to connect to endpoint. Is the {0} instance running?", ReaderName);
            return;
        }

        var doLoop = true;
        do
        {
            Console.Write("Message to send ("exit" to quit): ");
            var line = Console.ReadLine();
            sender.Send(Encoding.ASCII.GetBytes(line));
            doLoop = (line != "exit");

        } while (doLoop);
        sender.Shutdown(SocketShutdown.Both);
        sender.Close();
    }
}

We’ve reached the end of this post, but there’s more to come! Files and Sockets will seem like clubs and pitchforks compared to the rifles we have in store in the next Interprocess Communication Series installment. Check back, because we’ll be ramping up our arsenal and looking at Message Queues.

Stay tuned!

 

Phil Azzi, Developer, GeekHive

Phil Azzi

Technical Lead
Tags
  • Patterns & Practices
  • Theory
  • Tutorial

Recent Work

Check out what else we've been working on