C Sharpとプロセス間通信 - 名前付きパイプ

2025年12月9日 (火) 14:42時点におけるWiki (トーク | 投稿記録)による版 (ページの作成:「== 概要 == 名前付きパイプ (Named Pipe) は、プロセス間通信 (IPC) の一種であり、同一PC内または異なるPC間でのデータ通信を可能にする。<br> C#では、<code>System.IO.Pipes</code> 名前空間を使用して名前付きパイプを実装できる。<br> <br> 名前付きパイプの主な特徴を以下に示す。<br> * 双方向通信 *: クライアントとサーバ間で双方向のデータ送受信が可能 * スト…」)
(差分) ← 古い版 | 最新版 (差分) | 新しい版 → (差分)
📢 Webサイト閉鎖と移転のお知らせ
このWebサイトは2026年9月に閉鎖いたします。
新しい記事は移転先で追加しております。(旧サイトでは記事を追加しておりません)

概要

名前付きパイプ (Named Pipe) は、プロセス間通信 (IPC) の一種であり、同一PC内または異なるPC間でのデータ通信を可能にする。
C#では、System.IO.Pipes 名前空間を使用して名前付きパイプを実装できる。

名前付きパイプの主な特徴を以下に示す。

  • 双方向通信
    クライアントとサーバ間で双方向のデータ送受信が可能
  • ストリームベース
    データをバイトストリームとして扱う
  • セキュリティ
    アクセス制御リストによる接続制限が可能
  • 信頼性
    パイプ接続が確立されていることを保証
  • リモート対応
    SMB / CIFSプロトコルを使用して異なるPC間での通信も可能


名前付きパイプの構成要素を以下に示す。

  • サーバ側
    NamedPipeServerStream クラスを使用してパイプを作成して、クライアントからの接続を待機
  • クライアント側
    NamedPipeClientStream クラスを使用してサーバに接続
  • パイプ名
    パイプを識別するための一意の名前
  • 通信方向
    PipeDirection.In、PipeDirection.Out、PipeDirection.InOut



同一PC間の名前付きパイプ通信

サーバ側

NamedPipeServerStreamクラスは、名前付きパイプのサーバ側を実装するためのクラスである。
クライアントからの接続を待機して、データの送受信を行う。

NamedPipeServerStreamのコンストラクタ引数を以下に示す。

  • pipeName
    パイプの名前
    一意の識別子として使用される。
  • direction
    通信方向
    In (受信のみ)、Out (送信のみ)、InOut (双方向)
  • maxNumberOfServerInstances
    同時に作成できるサーバインスタンスの最大数
    1〜254、または、MaxAllowedServerInstances
  • transmissionMode
    転送モード
    Byte (バイトストリーム)、または、Message (メッセージ単位)
  • options
    パイプのオプション
    None、WriteThrough、Asynchronous等


主要なメソッドを以下に示す。

  • WaitForConnectionメソッド
    クライアントからの接続を同期的に待機
  • WaitForConnectionAsyncメソッド
    クライアントからの接続を非同期的に待機
  • Disconnectメソッド
    現在の接続を切断


主要なプロパティを以下に示す。

  • IsConnectedプロパティ
    クライアントが接続されているかどうか
  • TransmissionModeプロパティ
    現在の転送モード


 using System;
 using System.IO;
 using System.IO.Pipes;
 using System.Text;
 
 // 名前付きパイプサーバの作成
 using (var server = new NamedPipeServerStream(
    "MyPipe",                    // パイプ名
    PipeDirection.InOut,         // 双方向通信
    1,                           // 最大インスタンス数
    PipeTransmissionMode.Byte))  // 転送モード
 {
    Console.WriteLine("Waiting for client connection...");
 
    // クライアントの接続を待機
    server.WaitForConnection();
    Console.WriteLine("Client connected.");
 
    // データの受信
    using (var reader = new StreamReader(server))
    using (var writer = new StreamWriter(server) { AutoFlush = true })
    {
       string message = reader.ReadLine();
       Console.WriteLine($"Received: {message}");
 
       // データの送信
       writer.WriteLine("Hello from server!");
    }
 }


クライアント側

NamedPipeClientStreamクラスは、名前付きパイプのクライアント側を実装するためのクラスである。
指定されたパイプサーバに接続して、データの送受信を行う。

NamedPipeClientStreamのコンストラクタ引数を以下に示す。

  • serverName
    接続先のサーバ名
    ローカルの場合は、. を指定する。
  • pipeName
    接続先のパイプ名
  • direction
    通信方向
    In (受信のみ)、Out (送信のみ)、InOut (双方向)
  • options
    パイプのオプション
    None、WriteThrough、Asynchronous等
  • impersonationLevel
    偽装レベル
    リモート接続時に使用


主要なメソッドを以下に示す。

  • Connectメソッド
    サーバに同期的に接続する。(デフォルトタイムアウトあり)
  • Connect(int timeout)
    指定したタイムアウト (ミリ秒) でサーバに接続する。
  • ConnectAsyncメソッド
    サーバに非同期的に接続する。
  • ConnectAsync(int timeout)
    指定したタイムアウトでサーバに非同期接続する。


主要なプロパティを以下に示す。

  • IsConnectedプロパティ
    サーバに接続されているかどうか
  • NumberOfServerInstancesプロパティ
    サーバインスタンスの数


 using System;
 using System.IO;
 using System.IO.Pipes;
 using System.Text;
 
 // 名前付きパイプクライアントの作成
 using (var client = new NamedPipeClientStream(
    ".",                         // サーバ名 ("."はローカル)
    "MyPipe",                    // パイプ名
    PipeDirection.InOut))        // 双方向通信
 {
    Console.WriteLine("Connecting to server...");
 
    // サーバに接続 (タイムアウト: 5秒)
    client.Connect(5000);
    Console.WriteLine("Connected to server.");
 
    // データの送信
    using (var writer = new StreamWriter(client) { AutoFlush = true })
    using (var reader = new StreamReader(client))
    {
       writer.WriteLine("Hello from client!");
 
       // データの受信
       string response = reader.ReadLine();
       Console.WriteLine($"Received: {response}");
    }
 }


バイナリデータの送受信

バイナリデータを送受信する場合は、StreamReaderやStreamWriterではなく、Read メソッド、Write メソッドを使用する。

主要なバイナリ操作メソッドを以下に示す。

  • Write(byte[] buffer, int offset, int count)
    バイト配列からデータを書き込む。
  • Read(byte[] buffer, int offset, int count)
    バイト配列にデータを読み込む。
    戻り値は実際に読み込んだバイト数
  • WriteAsync(byte[] buffer, int offset, int count)
    バイト配列からデータを非同期的に書き込む。
  • ReadAsync(byte[] buffer, int offset, int count)
    バイト配列にデータを非同期的に読み込む。


 // サーバ側 : バイナリデータの送信
 using (var server = new NamedPipeServerStream("MyPipe", PipeDirection.Out))
 {
    server.WaitForConnection();
 
    byte[] data = new byte[] { 0x01, 0x02, 0x03, 0x04, 0x05 };
    server.Write(data, 0, data.Length);
    server.Flush();
 }
 
 // クライアント側: バイナリデータの受信
 using (var client = new NamedPipeClientStream(".", "MyPipe", PipeDirection.In))
 {
    client.Connect();
 
    byte[] buffer = new byte[5];
    int bytesRead = client.Read(buffer, 0, buffer.Length);
    Console.WriteLine($"Received {bytesRead} bytes");
 
    // データの表示
    foreach (byte b in buffer)
    {
       Console.Write($"0x{b:X2} ");
    }
 }


非同期通信

非同期メソッドを使用することにより、UIスレッドをブロックせずに通信を行うことができる。
非同期パターンでは、async / awaitキーワードを使用する。

主な非同期メソッドを以下に示す。

  • WaitForConnectionAsyncメソッド
    クライアントの接続を非同期的に待機する。
  • ConnectAsync(int timeout)メソッド
    サーバへの接続を非同期的に実行する。
  • ReadAsync(byte[] buffer, int offset, int count)メソッド
    データの非同期読み込み
  • WriteAsync(byte[] buffer, int offset, int count)メソッド
    データの非同期書き込み


StreamReaderとStreamWriterの非同期メソッドを以下に示す。

  • ReadLineAsyncメソッド
    1行を非同期的に読み込む。
  • WriteLineAsync(string value)メソッド
    1行を非同期的に書き込む。


 // サーバ側 : 非同期接続待機
 using (var server = new NamedPipeServerStream("MyPipe", PipeDirection.InOut))
 {
    Console.WriteLine("Waiting for client...");
    await server.WaitForConnectionAsync();
    Console.WriteLine("Client connected.");
 
    using (var writer = new StreamWriter(server) { AutoFlush = true })
    {
       await writer.WriteLineAsync("Hello from async server!");
    }
 }
 
 // クライアント側 : 非同期接続
 using (var client = new NamedPipeClientStream(".", "MyPipe", PipeDirection.InOut))
 {
    await client.ConnectAsync(5000);
 
    using (var reader = new StreamReader(client))
    {
       string message = await reader.ReadLineAsync();
       Console.WriteLine($"Received: {message}");
    }
 }


複数クライアントの対応

複数のクライアントからの接続を同時に処理する場合は、MaxAllowedServerInstances を指定して、接続ごとに新しいタスクを開始する。

NamedPipeServerStream.MaxAllowedServerInstances は、システムが許可する最大数のサーバインスタンスを表す定数である。

複数クライアント対応の実装手順を以下に示す。

  1. 無限ループでサーバインスタンスを作成する
  2. WaitForConnectionAsyncで接続を待機する
  3. 接続が確立したら、新しいTaskで処理を開始する。
  4. 次のクライアント向けに新しいサーバインスタンスを作成する。


 // 複数クライアント対応サーバ
 public async Task StartServerAsync()
 {
    while (true)
    {
       var server = new NamedPipeServerStream(
          "MyPipe",
          PipeDirection.InOut,
          NamedPipeServerStream.MaxAllowedServerInstances,
          PipeTransmissionMode.Byte,
          PipeOptions.Asynchronous);
 
       await server.WaitForConnectionAsync();
 
       // 各クライアントを別スレッドで処理
       _ = Task.Run(async () =>
       {
          try
          {
             using (server)
             using (var reader = new StreamReader(server))
             using (var writer = new StreamWriter(server) { AutoFlush = true })
             {
                while (server.IsConnected)
                {
                   string message = await reader.ReadLineAsync();
                   if (string.IsNullOrEmpty(message))
                      break;
 
                   Console.WriteLine($"Received: {message}");
 
                   // エコー応答
                   await writer.WriteLineAsync($"Echo: {message}");
                }
             }
          }
          catch (Exception ex)
          {
             Console.WriteLine($"Error: {ex.Message}");
          }
       });
    }
 }



異なるPC間の名前付きパイプ通信

リモート接続の概要

名前付きパイプは、異なるPC間での通信にも使用できる。
リモート接続には、SMB/CIFS (Server Message Block/Common Internet File System) プロトコルが使用される。

リモート接続の要件を以下に示す。

  • ネットワーク接続
    両PC間でTCP/IP接続が確立されていること。
  • ファイアウォール設定
    TCPポート445が開放されていること。
  • 認証
    適切なユーザ認証情報が必要。
  • SMB有効化
    Windowsのファイル共有機能が有効であること。


名前付きパイプのUNCパス形式を以下に示す。

  • ローカル
    \\.\pipe\PipeName
  • リモート
    \\ServerName\pipe\PipeName


TokenImpersonationLevel列挙子

TokenImpersonationLevel 列挙子は、リモート接続時にサーバがクライアントの資格情報をどの程度使用できるかを指定する列挙型である。

TokenImpersonationLevel列挙子の値を以下に示す。

  • None
    偽装なし。
    サーバはクライアントを偽装できない。
  • Anonymous
    匿名レベル。
    サーバはクライアントを偽装できるが、クライアントに関する情報を取得できない。
  • Identification
    識別レベル。
    サーバはクライアントのIDを取得できるが、クライアントの権限でリソースにアクセスできない。(認証のみ)
  • Impersonation
    偽装レベル。
    サーバはクライアントの権限でローカルリソースにアクセスできる。(推奨)
  • Delegation
    委任レベル。
    サーバはクライアントの権限でリモートリソースにアクセスできる。(Active Directory環境で使用)


リモート接続では、通常Impersonationレベルを使用する。

リモートサーバへの接続

異なるPC上のパイプサーバに接続する場合は、NamedPipeClientStream クラスのコンストラクタでサーバ名を指定する。

サーバ名の指定方法を以下に示す。

  • コンピュータ名
    "ServerPC"のようなNetBIOS名
  • FQDN
    "server.domain.com"のような完全修飾ドメイン名
  • IPアドレス
    "192.168.1.100"のようなIPv4アドレス


リモート接続のタイムアウトは、ネットワーク遅延を考慮して10秒以上を推奨する。

 // リモートPC上のサーバに接続
 using (var client = new NamedPipeClientStream(
    "ServerPCName",              // リモートPCのコンピューター名またはIPアドレス
    "MyPipe",                    // パイプ名
    PipeDirection.InOut,         // 双方向通信
    PipeOptions.None,            // オプション
    TokenImpersonationLevel.Impersonation))  // 偽装レベル
 {
    Console.WriteLine("Connecting to remote server...");
 
    // 接続 (タイムアウト : 10秒)
    client.Connect(10000);
    Console.WriteLine("Connected to remote server.");
 
    using (var writer = new StreamWriter(client) { AutoFlush = true })
    using (var reader = new StreamReader(client))
    {
       writer.WriteLine("Hello from remote client!");
       string response = reader.ReadLine();
       Console.WriteLine($"Received: {response}");
    }
 }


リモート接続には、SMB/CIFSプロトコルが使用されるため、ファイアウォールでTCPポート445の通信を許可する必要がある。

サーバ側 (リモート対応)

サーバ側は、ローカル接続とリモート接続で基本的に同じである。
ただし、リモートクライアントからの接続を受け付けるには、Windowsのファイル共有が有効である必要がある。

サーバ側で特別な設定が不要な理由を以下に示す。

  • 名前付きパイプはSMBプロトコルを使用するため、Windowsのファイル共有機能を利用
  • パイプ名は自動的にネットワーク経由でアクセス可能になる
  • セキュリティは、Windowsの認証機構とアクセス制御リストで管理される


 // リモート接続に対応したサーバ
 using (var server = new NamedPipeServerStream(
    "MyPipe",
    PipeDirection.InOut,
    1,
    PipeTransmissionMode.Byte,
    PipeOptions.None))
 {
    Console.WriteLine("Waiting for remote client connection...");
    server.WaitForConnection();
    Console.WriteLine("Remote client connected.");
 
    using (var reader = new StreamReader(server))
    using (var writer = new StreamWriter(server) { AutoFlush = true })
    {
       string message = reader.ReadLine();
       Console.WriteLine($"Received from remote: {message}");
 
       writer.WriteLine("Hello from remote server!");
    }
 }


セキュリティ設定

PipeSecurity クラスを使用して、パイプへのアクセス制御を設定できる。
これにより、特定のユーザやグループのみがパイプに接続できるように制限できる。

PipeSecurityクラスの主要なメソッドを以下に示す。

  • AddAccessRule(PipeAccessRule rule)
    アクセス規則を追加する。
  • RemoveAccessRule(PipeAccessRule rule)
    アクセス規則を削除する。
  • SetAccessRule(PipeAccessRule rule)
    アクセス規則を設定する。(既存の規則を置き換え)


PipeAccessRuleクラスのコンストラクタ引数を以下に示す。

  • identity (IdentityReference)
    アクセスを許可または拒否するユーザまたはグループ
  • rights (PipeAccessRights)
    アクセス権限 (ReadWrite、FullControl等)
  • type (AccessControlType)
    Allow (許可)、または、Deny (拒否)


WellKnownSidTypeの主な値を以下に示す。

  • WorldSid
    全てのユーザ
  • AuthenticatedUserSid
    認証されたユーザ
  • LocalSystemSid
    ローカルシステム
  • AdministratorsSid
    管理者グループ


 using System.IO.Pipes;
 using System.Security.AccessControl;
 using System.Security.Principal;
 
 // アクセス制御を持つサーバの作成
 var pipeSecurity = new PipeSecurity();
 
 // 認証されたユーザにアクセス権を付与
 var identity = new SecurityIdentifier(WellKnownSidType.AuthenticatedUserSid, null);
 var accessRule = new PipeAccessRule(
    identity,
    PipeAccessRights.ReadWrite,
    AccessControlType.Allow);
 
 pipeSecurity.AddAccessRule(accessRule);
 
 // セキュリティ設定を適用したサーバ
 using (var server = new NamedPipeServerStream(
    "SecurePipe",
    PipeDirection.InOut,
    1,
    PipeTransmissionMode.Byte,
    PipeOptions.None,
    4096,    // 入力バッファサイズ
    4096,    // 出力バッファサイズ
    pipeSecurity))
 {
    server.WaitForConnection();
    Console.WriteLine("Secure connection established.");
 }


特定ユーザのみ許可する設定

特定のユーザアカウントのみにアクセスを許可する例を以下に示す。

NTAccount クラスは、ユーザ名またはグループ名を表すクラスである。
コンストラクタには、"DOMAIN\Username"または"COMPUTER\Username"形式で指定する。

 // 特定ユーザのみアクセス許可
 var pipeSecurity = new PipeSecurity();
 
 // 特定ユーザに読み書き権限を付与
 var userIdentity = new NTAccount("DOMAIN\\Username");
 var userRule = new PipeAccessRule(
    userIdentity,
    PipeAccessRights.ReadWrite,
    AccessControlType.Allow);
 
 pipeSecurity.AddAccessRule(userRule);
 
 // 管理者にフルコントロール権限を付与
 var adminIdentity = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null);
 var adminRule = new PipeAccessRule(
    adminIdentity,
    PipeAccessRights.FullControl,
    AccessControlType.Allow);
 
 pipeSecurity.AddAccessRule(adminRule);
 
 using (var server = new NamedPipeServerStream(
    "SecurePipe",
    PipeDirection.InOut,
    1,
    PipeTransmissionMode.Byte,
    PipeOptions.None,
    4096,
    4096,
    pipeSecurity))
 {
    Console.WriteLine("Waiting for authorized client...");
    server.WaitForConnection();
    Console.WriteLine("Authorized client connected.");
 }



エラーハンドリング

基本的な例外処理

名前付きパイプ通信で発生する主な例外を以下に示す。

  • TimeoutException
    接続がタイムアウトした場合にスローされる。
  • IOException
    I/O操作中にエラーが発生した場合にスローされる。
  • UnauthorizedAccessException
    アクセス権限が不足している場合にスローされる。
  • InvalidOperationException
    無効な操作を実行しようとした場合にスローされる。(例 : 接続前にデータを送信)
  • ObjectDisposedException
    破棄されたオブジェクトにアクセスしようとした場合にスローされる。


 try
 {
    using (var client = new NamedPipeClientStream("ServerName", "MyPipe", PipeDirection.InOut))
    {
       client.Connect(5000);
 
       // 通信処理
       using (var writer = new StreamWriter(client) { AutoFlush = true })
       using (var reader = new StreamReader(client))
       {
          writer.WriteLine("Test message");
          string response = reader.ReadLine();
       }
    }
 }
 catch (TimeoutException)
 {
    Console.WriteLine("接続がタイムアウトしました。");
 }
 catch (IOException ex)
 {
    Console.WriteLine($"IO エラー: {ex.Message}");
 }
 catch (UnauthorizedAccessException)
 {
    Console.WriteLine("アクセスが拒否されました。管理者権限が必要な可能性があります。");
 }
 catch (Exception ex)
 {
    Console.WriteLine($"予期しないエラー: {ex.Message}");
 }


接続状態の確認

IsConnected プロパティを使用して、パイプの接続状態を確認できる。

主要なプロパティを以下に示す。

  • IsConnectedプロパティ
    クライアントまたはサーバが接続されているかどうか
  • TransmissionModeプロパティ
    現在の転送モード (ByteまたはMessage)
  • ReadMode
    現在の読み取りモード
  • InBufferSize
    入力バッファのサイズ (バイト単位)
  • OutBufferSize
    出力バッファのサイズ (バイト単位)


 using (var server = new NamedPipeServerStream("MyPipe", PipeDirection.InOut))
 {
    // 接続前の状態確認
    Console.WriteLine($"Initial Connected: {server.IsConnected}");
 
    server.WaitForConnection();
 
    // 接続後の状態確認
    Console.WriteLine($"Connected: {server.IsConnected}");
    Console.WriteLine($"Transmission Mode: {server.TransmissionMode}");
    Console.WriteLine($"Read Mode: {server.ReadMode}");
    Console.WriteLine($"In Buffer Size: {server.InBufferSize}");
    Console.WriteLine($"Out Buffer Size: {server.OutBufferSize}");
 }


リトライ処理

接続失敗時に再試行するリトライ処理の例を以下に示す。

リトライロジックの実装ポイントを以下に示す。

  • 最大リトライ回数を設定
  • 各リトライ間に適切な待機時間を設定
  • 接続成功時はループを抜ける
  • 全て失敗した場合はエラーをスロー


 public async Task<bool> ConnectWithRetryAsync(string serverName, string pipeName, int maxRetries = 3)
 {
    int retryCount = 0;
    int delayMs = 1000;
 
    while (retryCount < maxRetries)
    {
       try
       {
          var client = new NamedPipeClientStream(serverName, pipeName, PipeDirection.InOut);
          await client.ConnectAsync(5000);
 
          Console.WriteLine("接続成功");
          return true;
       }
       catch (TimeoutException)
       {
          retryCount++;
          Console.WriteLine($"接続タイムアウト。リトライ {retryCount}/{maxRetries}");
 
          if (retryCount < maxRetries)
          {
             await Task.Delay(delayMs);
             delayMs *= 2;  // 指数バックオフ
          }
       }
       catch (Exception ex)
       {
          Console.WriteLine($"接続エラー: {ex.Message}");
          throw;
       }
    }
 
    Console.WriteLine("最大リトライ回数に到達しました。");
    return false;
 }



リモート接続時の注意事項

ネットワーク要件

リモート接続に必要なネットワーク設定を以下に示す。

  • SMB/CIFSプロトコルの有効化
    Windowsのファイル共有機能が有効である必要がある。
  • ファイアウォール設定
    TCPポート445を開放する必要がある。
  • ネットワーク接続
    両PC間でネットワーク疎通が確保されていること。
  • 名前解決
    コンピューター名またはIPアドレスで相互にアクセス可能であること。


Linuxでポート445を開放する手順を以下に示す。

# Firewalldを使用している場合
sudo firewall-cmd --permanent --add-port=445/tcp
sudo firewall-cmd --reload

# UFWを使用している場合
sudo ufw allow 445/tcp


WindowsファイアウォールでTCPポート 445番を開放する手順を以下に示す。

  1. コントロールパネルから[Windowsファイアウォール]を開く。
  2. [詳細設定]を選択する。
  3. [受信の規則]から[新しい規則]を作成する。
  4. ポート445を許可する規則を追加する。


セキュリティ要件

リモート接続時のセキュリティに関する注意事項を以下に示す。

  • 認証
    リモート接続には適切なユーザ認証が必要する。
  • アクセス権限
    パイプへのアクセス権限を適切に設定する。
  • 暗号化
    名前付きパイプ自体は暗号化されないため、機密データにはSSL/TLSの使用を検討する。
  • 監査
    重要なシステムでは、接続ログを記録することを推奨する。


名前付きパイプはSMBプロトコルを使用するため、SMBのセキュリティ設定が適用される。
SMB 3.0以降では、暗号化オプションが利用可能である。

パフォーマンスと代替手段

リモート接続時のパフォーマンス特性と代替手段を以下に示す。

  • レイテンシ
    ネットワーク経由のためローカル通信より遅延が大きい。(数ミリ秒から数十ミリ秒)
  • スループット
    大量データ転送には向かない場合がある。(ネットワーク帯域幅に依存)
  • オーバーヘッド
    SMBプロトコルのオーバーヘッドが存在する。


代替通信手段の選択基準を以下に示す。

  • WCF (Windows Communication Foundation)
    エンタープライズレベルのサービス指向アーキテクチャ
  • gRPC
    高性能なRPCフレームワーク、Protocol Buffersを使用
  • REST API
    HTTP/HTTPSベースの標準的なWeb API
  • WebSocket
    双方向リアルタイム通信
  • TCP/UDP ソケット
    低レベルな直接通信、最大限の制御が可能
  • SignalR
    リアルタイムWeb通信フレームワーク


※注意
異なるPC間の通信には、WCF、gRPC、REST API、WebSocket等も検討すること。
特に、大量データ転送や低レイテンシが要求される場合は、これらの代替手段が適している。


サンプルコード

サーバアプリケーション

複数のクライアントからの接続を処理し、エコーサーバとして動作する例を以下に示す。

このサンプルの特徴を以下に示す。

  • 複数クライアント対応
    無限ループで新しい接続を受け付ける。
  • 非同期処理
    各クライアントを別タスクで処理する。
  • エラーハンドリング
    例外を適切に処理し、サーバが停止しないようにする。
  • タイムスタンプ付きログ
    接続と切断のタイミングを記録する。


 using System;
 using System.IO;
 using System.IO.Pipes;
 using System.Threading.Tasks;
 
 class PipeServer
 {
    static async Task Main(string[] args)
    {
       Console.WriteLine("=== Named Pipe Server ===");
       Console.WriteLine("Waiting for client connections...\n");
 
       while (true)
       {
          try
          {
             using (var server = new NamedPipeServerStream(
                "MyApplicationPipe",
                PipeDirection.InOut,
                NamedPipeServerStream.MaxAllowedServerInstances,
                PipeTransmissionMode.Byte,
                PipeOptions.Asynchronous))
             {
                await server.WaitForConnectionAsync();
                Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Client connected.");
 
                _ = Task.Run(async () =>
                {
                   try
                   {
                      using (var reader = new StreamReader(server))
                      using (var writer = new StreamWriter(server) { AutoFlush = true })
                      {
                         while (server.IsConnected)
                         {
                            string message = await reader.ReadLineAsync();
                            if (string.IsNullOrEmpty(message))
                               break;
 
                            Console.WriteLine($"Received: {message}");
 
                            // エコー応答
                            await writer.WriteLineAsync($"Echo: {message}");
                         }
                      }
                   }
                   catch (Exception ex)
                   {
                      Console.WriteLine($"Error: {ex.Message}");
                   }
                   finally
                   {
                      Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] Client disconnected.\n");
                   }
                });
             }
          }
          catch (Exception ex)
          {
             Console.WriteLine($"Server error: {ex.Message}");
          }
       }
    }
 }


クライアントアプリケーション

ローカルまたはリモートのサーバに接続して、対話的にメッセージを送信する例を以下に示す。

このサンプルの特徴を以下に示す。

  • ローカル/リモート対応
    ユーザ入力でサーバ名を指定可能
  • 対話的インターフェース
    ユーザがメッセージを入力して送信する。
  • エラーハンドリング
    タイムアウトやその他のエラーを処理する。
  • 終了処理
    exitコマンドで正常に終了する。


 using System;
 using System.IO;
 using System.IO.Pipes;
 
 class PipeClient
 {
    static void Main(string[] args)
    {
       Console.WriteLine("=== Named Pipe Client ===");
       Console.Write("Server name (. for local): ");
       string serverName = Console.ReadLine();
 
       if (string.IsNullOrWhiteSpace(serverName))
          serverName = ".";
 
       try
       {
          using (var client = new NamedPipeClientStream(
             serverName,
             "MyApplicationPipe",
             PipeDirection.InOut))
          {
             Console.WriteLine("Connecting to server...");
             client.Connect(5000);
             Console.WriteLine("Connected successfully.\n");
 
             using (var writer = new StreamWriter(client) { AutoFlush = true })
             using (var reader = new StreamReader(client))
             {
                while (true)
                {
                   Console.Write("Enter message (or 'exit' to quit): ");
                   string message = Console.ReadLine();
 
                   if (message?.ToLower() == "exit")
                      break;
 
                   writer.WriteLine(message);
 
                   string response = reader.ReadLine();
                   Console.WriteLine($"Server response: {response}\n");
                }
             }
          }
       }
       catch (TimeoutException)
       {
          Console.WriteLine("Connection timeout. Make sure the server is running.");
       }
       catch (Exception ex)
       {
          Console.WriteLine($"Error: {ex.Message}");
       }
 
       Console.WriteLine("\nPress any key to exit...");
       Console.ReadKey();
    }
 }