Using anonymous pipes for inter and intra process communication

Pipes are a mechanism for inter process communication in windows.Pipes come in two varieties viz. Anonymous pipes and Named pipes.Anonymous pipes as name suggest do not have a name and can be used to communicate between threads or two related processes i.e. when one process initiates another process.

Below are major differences between named and anonymous pipes.

Anonymous

Named

Communicate between threads or related process i.e. parent –child  processes on same machine. Communicate between any kind of process (related or unrelated) on a machine or over network.
One way communication (Half-duplex) i.e. either Server to client or client to server.* Two way communication (Duplex)
Byte based i.e. data is written and read as stream of bytes (or characters) Message based i.e.data is written and read as stream of messages(or string)

* You can always use two anonymous pipes for bi-directional communication.

In this blog post I will talk about anonymous pipes and in upcoming post will be talking more about named pipes.

To communicate using anonymous pipes within the process or between the process we need a mechanism to pass pipe handle to client so that it can connect to the pipe created by server.Below are the steps.

  1. Parent creates Pipe hence its called pipe server.
  2. Get the handle for the pipe.
  3. Parent creates child and pass pipe handle to child
  4. Child uses handle to connect to the pipe

Now communication can start.Based on what communication direction server and client specified one end of pipe would be considered Writer end and another one reader.

Lets see how this works in two different scenarios.

Intra-Process Communication

You can use anonymous pipes to communicate between two threads in the same process.Below is the code for the same.

using System;
using System.IO;
using System.IO.Pipes;
using System.Threading;

namespace AnonymousPipesIntraProcess
{
 class Program
 {
 
 static void Main(string[] args)
 {
 using (AnonymousPipeServerStream pipedServer = new AnonymousPipeServerStream(PipeDirection.Out))
 {

 Thread child = new Thread(new ParameterizedThreadStart(childThread));
 child.Start(pipedServer.GetClientHandleAsString());
 
 using (StreamWriter sw = new StreamWriter(pipedServer))
 {
 var data = string.Empty;
 sw.AutoFlush = true;
 while (!data.Equals("quit", StringComparison.InvariantCultureIgnoreCase))
 {
 pipedServer.WaitForPipeDrain();
 Console.WriteLine("SERVER : ");
 data = Console.ReadLine();
 sw.WriteLine(data);
 }


 }

 }
 }

 public static void childThread(object parentHandle)
 {
 using (AnonymousPipeClientStream pipedClient = new AnonymousPipeClientStream(PipeDirection.In, parentHandle.ToString()))
 {
 using (StreamReader reader = new StreamReader(pipedClient))
 {
 var data = string.Empty;
 while ((data = reader.ReadLine()) != null)
 {
 Console.WriteLine("CLIENT:" + data.ToString());
 }
 Console.Write("[CLIENT] Press Enter to continue...");
 Console.ReadLine();
 }
 }
 }

 
 }
}

Code is self explanatory only thing to note is when we define the Pipe server and Pipe client we specify direction i.e. whether in or out and further communication can take place only in that specified one direction.

Inter-Process Communication

Little variation of the same code can be used to communicate between two related processes i.e. when one process creates another process.

Server code:

using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;

namespace AnonymousPipesServer
{
 class Program
 {
 static void Main(string[] args)
 {
 
 
 using (AnonymousPipeServerStream pipedServer = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable))
 {
 var startInfo = new ProcessStartInfo(@"AnonymousPipesClient.exe");
 startInfo.UseShellExecute =false;
 
 startInfo.Arguments = pipedServer.GetClientHandleAsString();
 var client = Process.Start(startInfo);
 pipedServer.DisposeLocalCopyOfClientHandle();
 using (StreamWriter sw = new StreamWriter(pipedServer))
 {
 var data = string.Empty;
 sw.AutoFlush = true;
 while (!data.Equals("quit", StringComparison.InvariantCultureIgnoreCase))
 {
 pipedServer.WaitForPipeDrain();
 data = Console.ReadLine();
 sw.WriteLine(data);
 }
 }
 }
 }
 }
}

Client Code:

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Pipes;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace AnonymousPipesClient
{
 class Program
 {
 static void Main(string[] args)
 {
 
 var parentHandle = args[0];
 using (AnonymousPipeClientStream pipedClient = new AnonymousPipeClientStream(PipeDirection.In, parentHandle.ToString()))
 {
 using (StreamReader reader = new StreamReader(pipedClient))
 {
 
 var data = string.Empty;
 while ((data = reader.ReadLine()) != null)
 {
 Console.WriteLine("CLIENT:" + data.ToString());
 }
 Console.Write("[CLIENT] Press Enter to continue...");
 Console.ReadLine();
 }
 }

 }
 }
}

Two changes in this code are use of handle inheritance and DisposeLocalCopyOfClientHandle.

While creating object of AnonymousPipedServerStream  the second parameter specified in the constructor indicates whether or not child process can inherit the handles from parent process.This value should be HandleInheritability.Inheritable so that child process can use the pipe handle we are passing.Refer this link to know more about handle inheritance concept.

Also in case of inter process communication we need to call DisposeLocalCopyOfClientHandle method after passing handle to client as this makes sure server gets notification when client disposes its PipeStream object.Suppose you don’t call this method and client disposes its PipeStream object.In this case when server sends message to client ,server process will hang indefinitely. On the other hand by calling this method we insure that server will throw appropriate exception

Conclusion

Anonymous pipes provide an easy way of communicating between related processes without much overhead as in case of named pipes.There are also ways to make it work for unrelated process but that is not in scope of this post and also in such cases it is better to use named pipes.

 

 

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>