from MSDN
命名管道提供的功能比匿名管道多。 其功能包括通过网络进行全双工通信和多个服务器实例;基于消息的通信;以及客户端模拟,这使得连接进程可在远程服务器上使用其自己的权限集。
下面的示例演示如何使用 NamedPipeServerStream 类创建命名管道。 在此示例中,服务器进程创建了四个线程。 每个线程可以接受一个客户端连接。 连接的客户端进程随后向服务器提供一个文件名。 如果客户端具有足够的权限,服务器进程就会打开文件并将其内容发送回客户端。
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); } }
下面的示例演示使用 NamedPipeClientStream 类的客户端进程。 客户端连接服务器进程并向服务器发送一个文件名。 该示例使用模拟,所以运行客户端应用程序的标识必须具有访问文件的权限。 服务器随后将文件内容发送回客户端。 文件内容随后显示在控制台上。
using System; using System.IO; using System.IO.Pipes; using System.Text; using System.Security.Principal; using System.Diagnostics; 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") { NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation); Console.WriteLine("Connecting to server...\n"); pipeClient.Connect(); StreamString 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() { int i; string currentProcessName = Environment.CommandLine; 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); 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[{0}] has exited.", plist[j].Id); 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(); 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; } }
此示例中的客户端进程和服务器进程预期在同一台计算机上运行,因此提供给 NamedPipeClientStream 对象的服务器名称是 "."。 如果客户端进程和服务器进程位于不同的计算机上,则应该用运行服务器进程的计算机的网络名称来替换 "."。