Named Pipe Communication in C# Explained

This article provides an overview of pipe-based communication in C#, focusing specifically on named pipes as an efficient Inter-Process Communication (IPC) mechanism. You will learn what named pipes are, how they function within the .NET framework, the core classes used for implementation, and the primary advantages they offer over other communication methods.

What Is a Named Pipe?

A named pipe is a shared memory channel used for Inter-Process Communication (IPC). It allows two or more distinct processes—either running on the same local machine or across a local area network—to exchange data seamlessly. Unlike anonymous pipes, which are typically restricted to communication between parent and child processes on the same system, named pipes are identified by a unique system-wide name and can accept connections from any process that has the appropriate security permissions.

Named pipes operate in a client-server architecture. A server process creates the pipe and waits for a client process to establish a connection. Once connected, communication can be unidirectional (one-way) or duplex (two-way/bidirectional), allowing streams of bytes or discrete message packets to flow between the applications.

Core Classes in .NET

The .NET runtime provides built-in support for named pipes through the System.IO.Pipes namespace. The two primary classes used are:

  • NamedPipeServerStream: Used by the server application to construct the pipe instance, listen for incoming connections via WaitForConnection() or WaitForConnectionAsync(), and handle data transmission.
  • NamedPipeClientStream: Used by the client application to locate and connect to the listening server pipe using the designated pipe name and server name (using . for the local computer).

Because both classes inherit from System.IO.Stream, standard stream abstractions like StreamReader, StreamWriter, and binary buffers can be used to read and write data directly over the pipe.

How Named Pipe Communication Works

  1. Creation: The server initializes a NamedPipeServerStream by specifying a unique pipe name and communication direction (in, out, or in-out).
  2. Listening: The server calls WaitForConnection(), which pauses execution until a client connects.
  3. Connection: The client initializes a NamedPipeClientStream targeting the matching pipe name and calls Connect().
  4. Data Exchange: Once the link is established, both sides use standard I/O streams to serialize and deserialize messages.
  5. Termination: When operations conclude, both ends close and dispose of their stream instances, releasing system handles.

Basic Server Implementation Example

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

using var server = new NamedPipeServerStream("DemoPipe", PipeDirection.InOut);
server.WaitForConnection();

using var reader = new StreamReader(server);
using var writer = new StreamWriter(server) { AutoFlush = true };

string? clientMessage = reader.ReadLine();
writer.WriteLine($"Echo: {clientMessage}");

Basic Client Implementation Example

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

using var client = new NamedPipeClientStream(".", "DemoPipe", PipeDirection.InOut);
client.Connect();

using var reader = new StreamReader(client);
using var writer = new StreamWriter(client) { AutoFlush = true };

writer.WriteLine("Hello from Client");
string? serverResponse = reader.ReadLine();

Key Advantages of Named Pipes

  • High Performance: For local IPC, named pipes bypass the networking stack (TCP/IP), significantly reducing CPU overhead and latency compared to local sockets or HTTP services.
  • Flexible Data Modes: Pipes support both raw byte streams and message-oriented transmission, enabling applications to read variable-length data without manually parsing delimiters.
  • Built-in Security: Named pipes integrate with Windows Access Control Lists (ACLs), enabling developers to restrict which users or process identities can open and communicate across the pipe.