方法: ネットワークのプロセス間通信で名前付きパイプを使用する
名前付きパイプは、パイプ サーバーと 1 つ以上のパイプ クライアントとの間でのプロセス間通信を提供します。 名前付きパイプには、ローカル コンピューター上のプロセス間通信を提供する匿名パイプと比較して、より多くの機能が用意されています。 名前付きパイプは、ネットワーク上の複数のサーバー インスタンスでの全二重通信、メッセージ ベースの通信、およびクライアント偽装をサポートしています。偽装を使用すると、プロセスを接続してリモート サーバー上で独自のアクセス許可セットを使用できます。
重要
Linux 上の .NET は、これらの API の実装に Unix ドメイン ソケット (UDS) を使用します。
名前付きパイプを実装するには、NamedPipeServerStream クラスと NamedPipeClientStream クラスを使用します。
例 1
NamedPipeServerStream クラスを使用して、名前付きパイプを作成する例を次に示します。 この例では、サーバー プロセスは 4 つのスレッドを作成します。 各スレッドは、1 つのクライアント接続を受け入れます。 接続されたクライアント プロセスは、サーバーにファイル名を提供します。 クライアントに十分なアクセス許可がある場合は、サーバー プロセスはファイルを開き、その内容をクライアントに送り返します。
#using <System.Core.dll>
using namespace System;
using namespace System::IO;
using namespace System::IO::Pipes;
using namespace System::Text;
using namespace System::Threading;
// Defines the data protocol for reading and writing strings on our stream
public ref class StreamString
{
private:
Stream^ ioStream;
UnicodeEncoding^ streamEncoding;
public:
StreamString(Stream^ ioStream)
{
this->ioStream = ioStream;
streamEncoding = gcnew UnicodeEncoding();
}
String^ ReadString()
{
int len;
len = ioStream->ReadByte() * 256;
len += ioStream->ReadByte();
array<Byte>^ inBuffer = gcnew array<Byte>(len);
ioStream->Read(inBuffer, 0, len);
return streamEncoding->GetString(inBuffer);
}
int WriteString(String^ outString)
{
array<Byte>^ outBuffer = streamEncoding->GetBytes(outString);
int len = outBuffer->Length;
if (len > UInt16::MaxValue)
{
len = (int)UInt16::MaxValue;
}
ioStream->WriteByte((Byte)(len / 256));
ioStream->WriteByte((Byte)(len & 255));
ioStream->Write(outBuffer, 0, len);
ioStream->Flush();
return outBuffer->Length + 2;
}
};
// Contains the method executed in the context of the impersonated user
public ref class ReadFileToStream
{
private:
String^ fn;
StreamString ^ss;
public:
ReadFileToStream(StreamString^ str, String^ filename)
{
fn = filename;
ss = str;
}
void Start()
{
String^ contents = File::ReadAllText(fn);
ss->WriteString(contents);
}
};
public ref class PipeServer
{
private:
static int numThreads = 4;
public:
static void Main()
{
int i;
array<Thread^>^ servers = gcnew array<Thread^>(numThreads);
Console::WriteLine("\n*** Named pipe server stream with impersonation example ***\n");
Console::WriteLine("Waiting for client connect...\n");
for (i = 0; i < numThreads; i++)
{
servers[i] = gcnew Thread(gcnew ThreadStart(&ServerThread));
servers[i]->Start();
}
Thread::Sleep(250);
while (i > 0)
{
for (int j = 0; j < numThreads; j++)
{
if (servers[j] != nullptr)
{
if (servers[j]->Join(250))
{
Console::WriteLine("Server thread[{0}] finished.", servers[j]->ManagedThreadId);
servers[j] = nullptr;
i--; // decrement the thread watch count
}
}
}
}
Console::WriteLine("\nServer threads exhausted, exiting.");
}
private:
static void ServerThread()
{
NamedPipeServerStream^ pipeServer =
gcnew NamedPipeServerStream("testpipe", PipeDirection::InOut, numThreads);
int threadId = Thread::CurrentThread->ManagedThreadId;
// Wait for a client to connect
pipeServer->WaitForConnection();
Console::WriteLine("Client connected on thread[{0}].", threadId);
try
{
// Read the request from the client. Once the client has
// written to the pipe its security token will be available.
StreamString^ ss = gcnew StreamString(pipeServer);
// Verify our identity to the connected client using a
// string that the client anticipates.
ss->WriteString("I am the one true server!");
String^ filename = ss->ReadString();
// Read in the contents of the file while impersonating the client.
ReadFileToStream^ fileReader = gcnew ReadFileToStream(ss, filename);
// Display the name of the user we are impersonating.
Console::WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
filename, threadId, pipeServer->GetImpersonationUserName());
pipeServer->RunAsClient(gcnew PipeStreamImpersonationWorker(fileReader, &ReadFileToStream::Start));
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException^ e)
{
Console::WriteLine("ERROR: {0}", e->Message);
}
pipeServer->Close();
}
};
int main()
{
PipeServer::Main();
}
using System;
using System.IO;
using System.IO.Pipes;
using System.Text;
using System.Threading;
public class PipeServer
{
private static int numThreads = 4;
public static void Main()
{
int i;
Thread?[] servers = new Thread[numThreads];
Console.WriteLine("\n*** Named pipe server stream with impersonation example ***\n");
Console.WriteLine("Waiting for client connect...\n");
for (i = 0; i < numThreads; i++)
{
servers[i] = new Thread(ServerThread);
servers[i]?.Start();
}
Thread.Sleep(250);
while (i > 0)
{
for (int j = 0; j < numThreads; j++)
{
if (servers[j] != null)
{
if (servers[j]!.Join(250))
{
Console.WriteLine("Server thread[{0}] finished.", servers[j]!.ManagedThreadId);
servers[j] = null;
i--; // decrement the thread watch count
}
}
}
}
Console.WriteLine("\nServer threads exhausted, exiting.");
}
private static void ServerThread(object? data)
{
NamedPipeServerStream pipeServer =
new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);
int threadId = Thread.CurrentThread.ManagedThreadId;
// Wait for a client to connect
pipeServer.WaitForConnection();
Console.WriteLine("Client connected on thread[{0}].", threadId);
try
{
// Read the request from the client. Once the client has
// written to the pipe its security token will be available.
StreamString ss = new StreamString(pipeServer);
// Verify our identity to the connected client using a
// string that the client anticipates.
ss.WriteString("I am the one true server!");
string filename = ss.ReadString();
// Read in the contents of the file while impersonating the client.
ReadFileToStream fileReader = new ReadFileToStream(ss, filename);
// Display the name of the user we are impersonating.
Console.WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
filename, threadId, pipeServer.GetImpersonationUserName());
pipeServer.RunAsClient(fileReader.Start);
}
// Catch the IOException that is raised if the pipe is broken
// or disconnected.
catch (IOException e)
{
Console.WriteLine("ERROR: {0}", e.Message);
}
pipeServer.Close();
}
}
// Defines the data protocol for reading and writing strings on our stream
public class StreamString
{
private Stream ioStream;
private UnicodeEncoding streamEncoding;
public StreamString(Stream ioStream)
{
this.ioStream = ioStream;
streamEncoding = new UnicodeEncoding();
}
public string ReadString()
{
int len = 0;
len = ioStream.ReadByte() * 256;
len += ioStream.ReadByte();
byte[] inBuffer = new byte[len];
ioStream.Read(inBuffer, 0, len);
return streamEncoding.GetString(inBuffer);
}
public int WriteString(string outString)
{
byte[] outBuffer = streamEncoding.GetBytes(outString);
int len = outBuffer.Length;
if (len > UInt16.MaxValue)
{
len = (int)UInt16.MaxValue;
}
ioStream.WriteByte((byte)(len / 256));
ioStream.WriteByte((byte)(len & 255));
ioStream.Write(outBuffer, 0, len);
ioStream.Flush();
return outBuffer.Length + 2;
}
}
// Contains the method executed in the context of the impersonated user
public class ReadFileToStream
{
private string fn;
private StreamString ss;
public ReadFileToStream(StreamString str, string filename)
{
fn = filename;
ss = str;
}
public void Start()
{
string contents = File.ReadAllText(fn);
ss.WriteString(contents);
}
}
Imports System.IO
Imports System.IO.Pipes
Imports System.Text
Imports System.Threading
Public Class PipeServer
Private Shared numThreads As Integer = 4
Public Shared Sub Main()
Dim i As Integer
Dim servers(numThreads) As Thread
Console.WriteLine(vbCrLf + "*** Named pipe server stream with impersonation example ***" + vbCrLf)
Console.WriteLine("Waiting for client connect..." + vbCrLf)
For i = 0 To numThreads - 1
servers(i) = New Thread(AddressOf ServerThread)
servers(i).Start()
Next i
Thread.Sleep(250)
While i > 0
For j As Integer = 0 To numThreads - 1
If Not (servers(j) Is Nothing) Then
If servers(j).Join(250) Then
Console.WriteLine("Server thread[{0}] finished.", servers(j).ManagedThreadId)
servers(j) = Nothing
i -= 1 ' decrement the thread watch count
End If
End If
Next j
End While
Console.WriteLine(vbCrLf + "Server threads exhausted, exiting.")
End Sub
Private Shared Sub ServerThread(data As Object)
Dim pipeServer As New _
NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads)
Dim threadId As Integer = Thread.CurrentThread.ManagedThreadId
' Wait for a client to connect
pipeServer.WaitForConnection()
Console.WriteLine("Client connected on thread[{0}].", threadId)
Try
' Read the request from the client. Once the client has
' written to the pipe its security token will be available.
Dim ss As new StreamString(pipeServer)
' Verify our identity to the connected client using a
' string that the client anticipates.
ss.WriteString("I am the one true server!")
Dim filename As String = ss.ReadString()
' Read in the contents of the file while impersonating the client.
Dim fileReader As New ReadFileToStream(ss, filename)
' Display the name of the user we are impersonating.
Console.WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
filename, threadId, pipeServer.GetImpersonationUserName())
pipeServer.RunAsClient(AddressOf fileReader.Start)
' Catch the IOException that is raised if the pipe is broken
' or disconnected.
Catch e As IOException
Console.WriteLine("ERROR: {0}", e.Message)
End Try
pipeServer.Close()
End Sub
End Class
' Defines the data protocol for reading and writing strings on our stream
Public Class StreamString
Private ioStream As Stream
Private streamEncoding As UnicodeEncoding
Public Sub New(ioStream As Stream)
Me.ioStream = ioStream
streamEncoding = New UnicodeEncoding(False, False)
End Sub
Public Function ReadString() As String
Dim len As Integer = 0
len = CType(ioStream.ReadByte(), Integer) * 256
len += CType(ioStream.ReadByte(), Integer)
Dim inBuffer As Array = Array.CreateInstance(GetType(Byte), len)
ioStream.Read(inBuffer, 0, len)
Return streamEncoding.GetString(CType(inBuffer, Byte()))
End Function
Public Function WriteString(outString As String) As Integer
Dim outBuffer() As Byte = streamEncoding.GetBytes(outString)
Dim len As Integer = outBuffer.Length
If len > UInt16.MaxValue Then
len = CType(UInt16.MaxValue, Integer)
End If
ioStream.WriteByte(CType(len \ 256, Byte))
ioStream.WriteByte(CType(len And 255, Byte))
ioStream.Write(outBuffer, 0, outBuffer.Length)
ioStream.Flush()
Return outBuffer.Length + 2
End Function
End Class
' Contains the method executed in the context of the impersonated user
Public Class ReadFileToStream
Private fn As String
Private ss As StreamString
Public Sub New(str As StreamString, filename As String)
fn = filename
ss = str
End Sub
Public Sub Start()
Dim contents As String = File.ReadAllText(fn)
ss.WriteString(contents)
End Sub
End Class
例 2
次の例は、NamedPipeClientStream クラスを使用するクライアント プロセスを示したものです。 クライアントはサーバー プロセスに接続し、サーバーにファイル名を送信します。 この例では偽装を使用しているため、クライアント アプリケーションを実行している ID はファイルにアクセスする権限を持っている必要があります。 サーバーは、ファイルの内容をクライアントに送り返します。 その後、ファイルの内容がコンソールに表示されます。
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
using System.Security.Principal;
using System.Text;
using System.Threading;
public class PipeClient
{
private static int numClients = 4;
public static void Main(string[] args)
{
if (args.Length > 0)
{
if (args[0] == "spawnclient")
{
var pipeClient =
new NamedPipeClientStream(".", "testpipe",
PipeDirection.InOut, PipeOptions.None,
TokenImpersonationLevel.Impersonation);
Console.WriteLine("Connecting to server...\n");
pipeClient.Connect();
var ss = new StreamString(pipeClient);
// Validate the server's signature string.
if (ss.ReadString() == "I am the one true server!")
{
// The client security token is sent with the first write.
// Send the name of the file whose contents are returned
// by the server.
ss.WriteString("c:\\textfile.txt");
// Print the file to the screen.
Console.Write(ss.ReadString());
}
else
{
Console.WriteLine("Server could not be verified.");
}
pipeClient.Close();
// Give the client process some time to display results before exiting.
Thread.Sleep(4000);
}
}
else
{
Console.WriteLine("\n*** Named pipe client stream with impersonation example ***\n");
StartClients();
}
}
// Helper function to create pipe client processes
private static void StartClients()
{
string currentProcessName = Environment.CommandLine;
// Remove extra characters when launched from Visual Studio
currentProcessName = currentProcessName.Trim('"', ' ');
currentProcessName = Path.ChangeExtension(currentProcessName, ".exe");
Process?[] plist = new Process?[numClients];
Console.WriteLine("Spawning client processes...\n");
if (currentProcessName.Contains(Environment.CurrentDirectory))
{
currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty);
}
// Remove extra characters when launched from Visual Studio
currentProcessName = currentProcessName.Replace("\\", String.Empty);
currentProcessName = currentProcessName.Replace("\"", String.Empty);
int i;
for (i = 0; i < numClients; i++)
{
// Start 'this' program but spawn a named pipe client.
plist[i] = Process.Start(currentProcessName, "spawnclient");
}
while (i > 0)
{
for (int j = 0; j < numClients; j++)
{
if (plist[j] != null)
{
if (plist[j]!.HasExited)
{
Console.WriteLine($"Client process[{plist[j]?.Id}] has exited.");
plist[j] = null;
i--; // decrement the process watch count
}
else
{
Thread.Sleep(250);
}
}
}
}
Console.WriteLine("\nClient processes finished, exiting.");
}
}
// Defines the data protocol for reading and writing strings on our stream.
public class StreamString
{
private Stream ioStream;
private UnicodeEncoding streamEncoding;
public StreamString(Stream ioStream)
{
this.ioStream = ioStream;
streamEncoding = new UnicodeEncoding();
}
public string ReadString()
{
int len;
len = ioStream.ReadByte() * 256;
len += ioStream.ReadByte();
var inBuffer = new byte[len];
ioStream.Read(inBuffer, 0, len);
return streamEncoding.GetString(inBuffer);
}
public int WriteString(string outString)
{
byte[] outBuffer = streamEncoding.GetBytes(outString);
int len = outBuffer.Length;
if (len > UInt16.MaxValue)
{
len = (int)UInt16.MaxValue;
}
ioStream.WriteByte((byte)(len / 256));
ioStream.WriteByte((byte)(len & 255));
ioStream.Write(outBuffer, 0, len);
ioStream.Flush();
return outBuffer.Length + 2;
}
}
Imports System.Diagnostics
Imports System.IO
Imports System.IO.Pipes
Imports System.Security.Principal
Imports System.Text
Imports System.Threading
Public Class PipeClient
Private Shared numClients As Integer = 4
Public Shared Sub Main(args() As String)
If args.Length > 0 Then
If args(0) = "spawnclient" Then
Dim pipeClient As New NamedPipeClientStream( _
".", "testpipe", _
PipeDirection.InOut, PipeOptions.None, _
TokenImpersonationLevel.Impersonation)
Console.WriteLine("Connecting to server..." + vbCrLf)
pipeClient.Connect()
Dim ss As New StreamString(pipeClient)
' Validate the server's signature string.
If ss.ReadString() = "I am the one true server!" Then
' The client security token is sent with the first write.
' Send the name of the file whose contents are returned
' by the server.
ss.WriteString("c:\textfile.txt")
' Print the file to the screen.
Console.Write(ss.ReadString())
Else
Console.WriteLine("Server could not be verified.")
End If
pipeClient.Close()
' Give the client process some time to display results before exiting.
Thread.Sleep(4000)
End If
Else
Console.WriteLine(vbCrLf + "*** Named pipe client stream with impersonation example ***" + vbCrLf)
StartClients()
End If
End Sub
' Helper function to create pipe client processes
Private Shared Sub StartClients()
Dim currentProcessName As String = Environment.CommandLine
Dim plist(numClients - 1) As Process
Console.WriteLine("Spawning client processes..." + vbCrLf)
If currentProcessName.Contains(Environment.CurrentDirectory) Then
currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty)
End If
' Remove extra characters when launched from Visual Studio.
currentProcessName = currentProcessName.Replace("\", String.Empty)
currentProcessName = currentProcessName.Replace("""", String.Empty)
' Change extension for .NET Core "dotnet run" returns the DLL, not the host exe.
currentProcessName = Path.ChangeExtension(currentProcessName, ".exe")
Dim i As Integer
For i = 0 To numClients - 1
' Start 'this' program but spawn a named pipe client.
plist(i) = Process.Start(currentProcessName, "spawnclient")
Next
While i > 0
For j As Integer = 0 To numClients - 1
If plist(j) IsNot Nothing Then
If plist(j).HasExited Then
Console.WriteLine($"Client process[{plist(j).Id}] has exited.")
plist(j) = Nothing
i -= 1 ' decrement the process watch count
Else
Thread.Sleep(250)
End If
End If
Next
End While
Console.WriteLine(vbCrLf + "Client processes finished, exiting.")
End Sub
End Class
' Defines the data protocol for reading and writing strings on our stream
Public Class StreamString
Private ioStream As Stream
Private streamEncoding As UnicodeEncoding
Public Sub New(ioStream As Stream)
Me.ioStream = ioStream
streamEncoding = New UnicodeEncoding(False, False)
End Sub
Public Function ReadString() As String
Dim len As Integer = 0
len = CType(ioStream.ReadByte(), Integer) * 256
len += CType(ioStream.ReadByte(), Integer)
Dim inBuffer As Array = Array.CreateInstance(GetType(Byte), len)
ioStream.Read(inBuffer, 0, len)
Return streamEncoding.GetString(CType(inBuffer, Byte()))
End Function
Public Function WriteString(outString As String) As Integer
Dim outBuffer As Byte() = streamEncoding.GetBytes(outString)
Dim len As Integer = outBuffer.Length
If len > UInt16.MaxValue Then
len = CType(UInt16.MaxValue, Integer)
End If
ioStream.WriteByte(CType(len \ 256, Byte))
ioStream.WriteByte(CType(len And 255, Byte))
ioStream.Write(outBuffer, 0, outBuffer.Length)
ioStream.Flush()
Return outBuffer.Length + 2
End Function
End Class
信頼性の高いプログラミング
ここで例示したクライアント プロセスとサーバー プロセスは、同じコンピューターで実行することを意図しているため、NamedPipeClientStream オブジェクトには、"."
というサーバー名が提供されます。 クライアント プロセスとサーバー プロセスを異なるコンピューター上で実行する場合、"."
はサーバー プロセスを実行するコンピューターのネットワーク名で置換されます。
関連項目
.NET