PR

【C#】プロセス間通信の基本を学ぼう!

【C#】
広告

プロセス間通信(IPC)は、異なるプロセス間でデータを交換するための重要な技術です。

C#は、複数のプロセス間で情報を共有または送信するための強力なツールを提供しており、この記事ではC#でのプロセス間通信の基本的な方法を紹介します。

具体的には、名前付きパイプ、メモリマップトファイル、ソケット通信を取り上げ、各方法の基本的な使用例を示します。

C#でのプロセス間通信の選択肢

C#で提供されているプロセス間通信のオプションは多岐にわたります。ここでは、最も一般的に使用される三つの技術に焦点を当てます:

  1. 名前付きパイプ
  2. メモリマップトファイル
  3. ソケット通信

これらの技術は、異なるシナリオに応じて選択することができ、それぞれに独自の利点と使用場面があります。

1. 名前付きパイプの使用

名前付きパイプは、同一マシン上のプロセス間でのデータ転送に最適です。C#ではSystem.IO.Pipes名前空間を使用して簡単に実装することができます。

サーバープロセスの実装例:
using System;
using System.IO.Pipes;

public class PipeServer
{
    public static void Main()
    {
        using (var pipeServer = new NamedPipeServerStream("testpipe"))
        {
            Console.WriteLine("Waiting for client connection...");
            pipeServer.WaitForConnection();

            using (StreamWriter writer = new StreamWriter(pipeServer))
            {
                writer.AutoFlush = true;
                writer.WriteLine("Hello, client!");
            }
        }
    }
}

コードの概要

  • 名前付きパイプのサーバーストリームの作成
  • クライアントの接続待ち
  • メッセージの送信

名前付きパイプのサーバーストリームの作成

using (var pipeServer = new NamedPipeServerStream("testpipe"))
{
    ...
}
  • NamedPipeServerStreamクラスは、サーバーサイドの名前付きパイプを表します。このインスタンスが名前付きパイプ通信のサーバー側のエンドポイントとして機能します。
  • "testpipe"はパイプの名前であり、クライアントがこの名前を使用して接続を試みることになります。名前付きパイプは、同じマシン上の異なるプロセス間、または信頼できるネットワーク上の異なるマシン間でデータをやり取りするのに使用されます。

クライアントの接続待ち

Console.WriteLine("Waiting for client connection...");
pipeServer.WaitForConnection();
  • サーバーはWaitForConnectionメソッドを呼び出してクライアントからの接続を同期的に待ちます。このメソッドはクライアントが接続するまでブロック(待機状態)されます。
  • 接続が確立されると、次のステップに進みます。この間、サーバーは接続の待機メッセージをコンソールに出力します。

メッセージの送信

using (StreamWriter writer = new StreamWriter(pipeServer))
{
    writer.AutoFlush = true;
    writer.WriteLine("Hello, client!");
}
  • StreamWriterは、pipeServer(名前付きパイプサーバーストリーム)上でテキストデータを書き込むために使用されます。ここでStreamWriterを使用することで、バイト配列を直接扱う代わりに、簡単にテキストデータをストリームに書き込むことができます。
  • AutoFlushプロパティがtrueに設定されているので、WriteLineメソッドを呼び出すたびにバッファが自動的にフラッシュされ、すぐにクライアントにデータが送信されます。
  • WriteLine("Hello, client!");はクライアントに送るメッセージです。
クライアントプロセスの実装例:
using System;
using System.IO.Pipes;
using System.IO;

public class PipeClient
{
    public static void Main()
    {
        using (var pipeClient = new NamedPipeClientStream("testpipe"))
        {
            pipeClient.Connect();
            Console.WriteLine("Connected to server.");
            using (StreamReader reader = new StreamReader(pipeClient))
            {
                string response = reader.ReadLine();
                Console.WriteLine("Received from server: " + response);
            }
        }
    }
}

コードの概要

  1. 名前付きパイプのクライアントストリームの作成
  2. サーバーへの接続
  3. メッセージの受信

名前付きパイプのクライアントストリームの作成

using (var pipeClient = new NamedPipeClientStream("testpipe"))
{
    ...
}
  • NamedPipeClientStreamクラスは、クライアント側の名前付きパイプ通信を担います。このインスタンスが名前付きパイプ通信のクライアント側のエンドポイントとして機能します。
  • "testpipe"はサーバー側で作成された名前付きパイプと同じ名前であり、この名前を通じてサーバーとの接続を試みます。

サーバーへの接続

pipeClient.Connect();
Console.WriteLine("Connected to server.");
  • Connectメソッドは、クライアントがサーバーの名前付きパイプに対して接続を試みるためのメソッドです。このメソッドはクライアントがサーバーに接続するまで実行をブロックします。
  • 接続が成功すると、”Connected to server.”というメッセージがコンソールに表示されます。

メッセージの受信

using (StreamReader reader = new StreamReader(pipeClient))
{
    string response = reader.ReadLine();
    Console.WriteLine("Received from server: " + response);
}
  • StreamReaderは、パイプからのデータ読み取りを行うために使用されます。ここではpipeClientストリームをStreamReaderにラップし、テキストベースのデータの読み取りを簡単に行います。
  • ReadLineメソッドはパイプからの一行を読み取ります。この場合、サーバーから送られたメッセージが読み取られます。
  • 読み取ったメッセージはコンソールに表示され、「Received from server:」というプレフィックスと共に出力されます。

2. メモリマップトファイルの使用

メモリマップトファイルは、大量のデータを高速に共有するのに適しています。これはファイルやメモリの一部をマッピングすることで実現します。

基本的な使用例:
using System;
using System.IO.MemoryMappedFiles;

public class MemoryMappedExample
{
    public static void Main()
    {
        using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 1024))
        {
            using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
            {
                accessor.Write(0, 12345);  // データを書き込む
            }
        }
    }
}

コードの概要

  1. メモリマップトファイルの作成
  2. メモリビューアクセッサの作成
  3. データの書き込み

メモリマップトファイルの作成

using (MemoryMappedFile mmf = MemoryMappedFile.CreateNew("testmap", 1024))
{
    ...
}
  • MemoryMappedFile.CreateNewメソッドは、新しいメモリマップトファイルを作成します。このメソッドの第一引数"testmap"はメモリマップトファイルの名前です。この名前を使用して、他のプロセスがこのメモリマップトファイルにアクセスできるようになります。
  • 第二引数1024はメモリマップトファイルのサイズをバイト単位で指定します。この例では、1024バイトのサイズが確保されます。

メモリビューアクセッサの作成

using (MemoryMappedViewAccessor accessor = mmf.CreateViewAccessor())
{
    ...
}
  • CreateViewAccessorメソッドは、メモリマップトファイル上にビューアクセッサを作成します。このアクセッサを通じて、メモリマップトファイルの内容を読み書きできます。
  • アクセッサはメモリマップトファイルの全体または一部に対するアクセスを提供し、データの読み書きに使用されます。

データの書き込み

accessor.Write(0, 12345);  // データを書き込む
  • Writeメソッドは、指定された位置にデータを書き込みます。第一引数0はデータを書き込むオフセット(開始位置)を示し、ここではファイルの最初からデータを書き始めることを意味します。
  • 第二引数12345は書き込むデータです。このメソッドは型安全であるため、ここでは整数を直接指定しています。

3. ソケット通信の使用

ソケット通信は、ネットワークを介して異なるマシン間で通信する場合に最も一般的に使用されます。C#のSystem.Net.Sockets名空間がこれをサポートします。

サーバーソケットの実装例:
using System;
using System.Net;
using System.Net.Sockets;
using System.Text;

public class SocketServer
{
    public static void Main()
    {
        var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
        var localEndPoint = new IPEndPoint(IPAddress.Any, 11000);
        server.Bind(localEndPoint);
        server.Listen(10);

        Console.WriteLine("Waiting for a connection...");
        Socket clientSocket = server.Accept();

        byte[] bytes = new byte[1024];
        int numByte = clientSocket.Receive(bytes);

        Console.WriteLine("Data received: " + Encoding.ASCII.GetString(bytes, 0, numByte));
        clientSocket.Shutdown(SocketShutdown.Both);
        clientSocket.Close();
    }
}

コードの概要

  1. ソケットの初期化
  2. サーバーのバインドとリスニング
  3. クライアントの接続受け入れ
  4. データの受信と表示
  5. ソケットのクローズ

ソケットの初期化

var server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  • Socket オブジェクトの作成: ここで使用される Socket クラスのコンストラクタは、通信に使用されるアドレスファミリー(AddressFamily.InterNetwork はIPv4を指します)、ソケットタイプ(SocketType.Stream は接続指向のソケットを示し、TCPプロトコルに適しています)、およびプロトコルタイプ(ProtocolType.Tcp)を引数に取ります。

サーバーのバインドとリスニング

var localEndPoint = new IPEndPoint(IPAddress.Any, 11000);
server.Bind(localEndPoint);
server.Listen(10);
  • IPEndPoint オブジェクトの作成: IPAddress.Any はすべてのローカルインターフェースで受信を待機することを示し、11000 はこのサーバーが使用するポート番号です。
  • Bind メソッドはサーバーを特定のローカルエンドポイントにバインドします。
  • Listen メソッドは、サーバーがクライアントからの接続をどれだけ待機できるか(この場合は最大10の接続)を設定します。

クライアントの接続受け入れ

Socket clientSocket = server.Accept();
  • Accept メソッドはクライアントからの接続を受け入れるために使用されます。このメソッドはクライアントが接続するまでブロックし、接続されたクライアント用の新しい Socket オブジェクトを返します。

データの受信と表示

byte[] bytes = new byte[1024];
int numByte = clientSocket.Receive(bytes);
Console.WriteLine("Data received: " + Encoding.ASCII.GetString(bytes, 0, numByte));
  • 受信用のバイト配列を初期化し、Receive メソッドを使ってデータを受信します。このメソッドは受信したバイト数を返します。
  • Encoding.ASCII.GetString メソッドはバイト配列をASCII文字列に変換し、受信したデータを表示します。

ソケットのクローズ

clientSocket.Shutdown(SocketShutdown.Both);
clientSocket.Close();
  • Shutdown メソッドは、双方向の送受信を終了するために使用されます。
  • Close メソッドはソケットの接続を閉じ、関連するリソースを解放します。

まとめ

C#にはプロセス間通信を行うための強力なオプションが豊富に用意されています。

適切な通信手段を選択することで、アプリケーションの効率と拡張性を向上させることができます。

本記事で紹介した各技術の基本的な使用法を理解し、自分のニーズに合った方法を選択してください。

広告
【C#】
広告
タイトルとURLをコピーしました