[IO]管道

  管道为进程间通信提供了平台。 管道分为两种类型:

  • 匿名管道。

  匿名管道在本地计算机上提供进程间通信。与命名管道相比,虽然匿名管道需要的开销更少,但提供的服务有限。 匿名管道是单向的,不能通过网络使用。 仅支持一个服务器实例。 匿名管道可用于线程间通信,也可用于父进程和子进程之间的通信,因为管道句柄可以轻松传递给所创建的子进程。

  可通过使用 AnonymousPipeServerStream 和 AnonymousPipeClientStream 类来实现匿名管道。

  下面的示例展示了如何使用匿名管道将字符串从父进程发送到子进程。 此示例在父进程中创建 AnonymousPipeServerStream 对象,它的 PipeDirection 值为 Out。 然后,父进程使用客户端句柄创建 AnonymousPipeClientStream 对象,以创建子进程。 子进程的 PipeDirection 值为 In

  下面的示例展示了服务器进程:

 1 using System;
 2 using System.IO;
 3 using System.IO.Pipes;
 4 using System.Diagnostics;
 5 
 6 class PipeServer
 7 {
 8     static void Main()
 9     {
10         Process pipeClient = new Process();
11 
12         pipeClient.StartInfo.FileName = "pipeClient.exe";
13 
14         using (AnonymousPipeServerStream pipeServer =
15             new AnonymousPipeServerStream(PipeDirection.Out,
16             HandleInheritability.Inheritable))
17         {
18             Console.WriteLine("[SERVER] Current TransmissionMode: {0}.",
19                 pipeServer.TransmissionMode);
20 
21             // Pass the client process a handle to the server.
22             pipeClient.StartInfo.Arguments =
23                 pipeServer.GetClientHandleAsString();
24             pipeClient.StartInfo.UseShellExecute = false;
25             pipeClient.Start();
26 
27             pipeServer.DisposeLocalCopyOfClientHandle();
28 
29             try
30             {
31                 // Read user input and send that to the client process.
32                 using (StreamWriter sw = new StreamWriter(pipeServer))
33                 {
34                     sw.AutoFlush = true;
35                     // Send a 'sync message' and wait for client to receive it.
36                     sw.WriteLine("SYNC");
37                     pipeServer.WaitForPipeDrain();
38                     // Send the console input to the client process.
39                     Console.Write("[SERVER] Enter text: ");
40                     sw.WriteLine(Console.ReadLine());
41                 }
42             }
43             // Catch the IOException that is raised if the pipe is broken
44             // or disconnected.
45             catch (IOException e)
46             {
47                 Console.WriteLine("[SERVER] Error: {0}", e.Message);
48             }
49         }
50 
51         pipeClient.WaitForExit();
52         pipeClient.Close();
53         Console.WriteLine("[SERVER] Client quit. Server terminating.");
54     }
55 }
View Code

  下面的示例展示了客户端进程。 服务器进程启动客户端进程,并为此进程提供客户端句柄。

 1 using System;
 2 using System.IO;
 3 using System.IO.Pipes;
 4 
 5 class PipeClient
 6 {
 7     static void Main(string[] args)
 8     {
 9         if (args.Length > 0)
10         {
11             using (PipeStream pipeClient =
12                 new AnonymousPipeClientStream(PipeDirection.In, args[0]))
13             {
14                 Console.WriteLine("[CLIENT] Current TransmissionMode: {0}.",
15                    pipeClient.TransmissionMode);
16 
17                 using (StreamReader sr = new StreamReader(pipeClient))
18                 {
19                     // Display the read text to the console
20                     string temp;
21 
22                     // Wait for 'sync message' from the server.
23                     do
24                     {
25                         Console.WriteLine("[CLIENT] Wait for sync...");
26                         temp = sr.ReadLine();
27                     }
28                     while (!temp.StartsWith("SYNC"));
29 
30                     // Read the server data and echo to the console.
31                     while ((temp = sr.ReadLine()) != null)
32                     {
33                         Console.WriteLine("[CLIENT] Echo: " + temp);
34                     }
35                 }
36             }
37         }
38         Console.Write("[CLIENT] Press Enter to continue...");
39         Console.ReadLine();
40     }
41 }
View Code
  • 命名管道。

  命名管道在管道服务器和一个或多个管道客户端之间提供进程间通信。 命名管道可以是单向的,也可以是双向的。 它们支持基于消息的通信,并允许多个客户端使用相同的管道名称同时连接到服务器进程。 命名管道还支持模拟,这样连接进程就可以在远程服务器上使用自己的权限。

  可通过使用 NamedPipeServerStream 和 NamedPipeClientStream 类来实现命名管道。

  下面的示例展示了如何使用 NamedPipeServerStream 类创建命名管道:

  1 using System;
  2 using System.IO;
  3 using System.IO.Pipes;
  4 using System.Text;
  5 using System.Threading;
  6 
  7 public class PipeServer
  8 {
  9     private static int numThreads = 4;
 10 
 11     public static void Main()
 12     {
 13         int i;
 14         Thread[] servers = new Thread[numThreads];
 15 
 16         Console.WriteLine("
*** Named pipe server stream with impersonation example ***
");
 17         Console.WriteLine("Waiting for client connect...
");
 18         for (i = 0; i < numThreads; i++)
 19         {
 20             servers[i] = new Thread(ServerThread);
 21             servers[i].Start();
 22         }
 23         Thread.Sleep(250);
 24         while (i > 0)
 25         {
 26             for (int j = 0; j < numThreads; j++)
 27             {
 28                 if (servers[j] != null)
 29                 {
 30                     if (servers[j].Join(250))
 31                     {
 32                         Console.WriteLine("Server thread[{0}] finished.", servers[j].ManagedThreadId);
 33                         servers[j] = null;
 34                         i--;    // decrement the thread watch count
 35                     }
 36                 }
 37             }
 38         }
 39         Console.WriteLine("
Server threads exhausted, exiting.");
 40     }
 41 
 42     private static void ServerThread(object data)
 43     {
 44         NamedPipeServerStream pipeServer =
 45             new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads);
 46 
 47         int threadId = Thread.CurrentThread.ManagedThreadId;
 48 
 49         // Wait for a client to connect
 50         pipeServer.WaitForConnection();
 51 
 52         Console.WriteLine("Client connected on thread[{0}].", threadId);
 53         try
 54         {
 55             // Read the request from the client. Once the client has
 56             // written to the pipe its security token will be available.
 57 
 58             StreamString ss = new StreamString(pipeServer);
 59 
 60             // Verify our identity to the connected client using a
 61             // string that the client anticipates.
 62 
 63             ss.WriteString("I am the one true server!");
 64             string filename = ss.ReadString();
 65 
 66             // Read in the contents of the file while impersonating the client.
 67             ReadFileToStream fileReader = new ReadFileToStream(ss, filename);
 68 
 69             // Display the name of the user we are impersonating.
 70             Console.WriteLine("Reading file: {0} on thread[{1}] as user: {2}.",
 71                 filename, threadId, pipeServer.GetImpersonationUserName());
 72             pipeServer.RunAsClient(fileReader.Start);
 73         }
 74         // Catch the IOException that is raised if the pipe is broken
 75         // or disconnected.
 76         catch (IOException e)
 77         {
 78             Console.WriteLine("ERROR: {0}", e.Message);
 79         }
 80         pipeServer.Close();
 81     }
 82 }
 83 
 84 // Defines the data protocol for reading and writing strings on our stream
 85 public class StreamString
 86 {
 87     private Stream ioStream;
 88     private UnicodeEncoding streamEncoding;
 89 
 90     public StreamString(Stream ioStream)
 91     {
 92         this.ioStream = ioStream;
 93         streamEncoding = new UnicodeEncoding();
 94     }
 95 
 96     public string ReadString()
 97     {
 98         int len = 0;
 99 
100         len = ioStream.ReadByte() * 256;
101         len += ioStream.ReadByte();
102         byte[] inBuffer = new byte[len];
103         ioStream.Read(inBuffer, 0, len);
104 
105         return streamEncoding.GetString(inBuffer);
106     }
107 
108     public int WriteString(string outString)
109     {
110         byte[] outBuffer = streamEncoding.GetBytes(outString);
111         int len = outBuffer.Length;
112         if (len > UInt16.MaxValue)
113         {
114             len = (int)UInt16.MaxValue;
115         }
116         ioStream.WriteByte((byte)(len / 256));
117         ioStream.WriteByte((byte)(len & 255));
118         ioStream.Write(outBuffer, 0, len);
119         ioStream.Flush();
120 
121         return outBuffer.Length + 2;
122     }
123 }
124 
125 // Contains the method executed in the context of the impersonated user
126 public class ReadFileToStream
127 {
128     private string fn;
129     private StreamString ss;
130 
131     public ReadFileToStream(StreamString str, string filename)
132     {
133         fn = filename;
134         ss = str;
135     }
136 
137     public void Start()
138     {
139         string contents = File.ReadAllText(fn);
140         ss.WriteString(contents);
141     }
142 }
View Code

  下面的示例展示了使用 NamedPipeClientStream 类的客户端进程。

  1 using System;
  2 using System.IO;
  3 using System.IO.Pipes;
  4 using System.Text;
  5 using System.Security.Principal;
  6 using System.Diagnostics;
  7 using System.Threading;
  8 
  9 public class PipeClient
 10 {
 11     private static int numClients = 4;
 12 
 13     public static void Main(string[] Args)
 14     {
 15         if (Args.Length > 0)
 16         {
 17             if (Args[0] == "spawnclient")
 18             {
 19                 NamedPipeClientStream pipeClient =
 20                     new NamedPipeClientStream(".", "testpipe",
 21                         PipeDirection.InOut, PipeOptions.None,
 22                         TokenImpersonationLevel.Impersonation);
 23 
 24                 Console.WriteLine("Connecting to server...
");
 25                 pipeClient.Connect();
 26 
 27                 StreamString ss = new StreamString(pipeClient);
 28                 // Validate the server's signature string
 29                 if (ss.ReadString() == "I am the one true server!")
 30                 {
 31                     // The client security token is sent with the first write.
 32                     // Send the name of the file whose contents are returned
 33                     // by the server.
 34                     ss.WriteString("c:\textfile.txt");
 35 
 36                     // Print the file to the screen.
 37                     Console.Write(ss.ReadString());
 38                 }
 39                 else
 40                 {
 41                     Console.WriteLine("Server could not be verified.");
 42                 }
 43                 pipeClient.Close();
 44                 // Give the client process some time to display results before exiting.
 45                 Thread.Sleep(4000);
 46             }
 47         }
 48         else
 49         {
 50             Console.WriteLine("
*** Named pipe client stream with impersonation example ***
");
 51             StartClients();
 52         }
 53     }
 54 
 55     // Helper function to create pipe client processes
 56     private static void StartClients()
 57     {
 58         int i;
 59         string currentProcessName = Environment.CommandLine;
 60         Process[] plist = new Process[numClients];
 61 
 62         Console.WriteLine("Spawning client processes...
");
 63 
 64         if (currentProcessName.Contains(Environment.CurrentDirectory))
 65         {
 66             currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty);
 67         }
 68 
 69         // Remove extra characters when launched from Visual Studio
 70         currentProcessName = currentProcessName.Replace("\", String.Empty);
 71         currentProcessName = currentProcessName.Replace(""", String.Empty);
 72 
 73         for (i = 0; i < numClients; i++)
 74         {
 75             // Start 'this' program but spawn a named pipe client.
 76             plist[i] = Process.Start(currentProcessName, "spawnclient");
 77         }
 78         while (i > 0)
 79         {
 80             for (int j = 0; j < numClients; j++)
 81             {
 82                 if (plist[j] != null)
 83                 {
 84                     if (plist[j].HasExited)
 85                     {
 86                         Console.WriteLine("Client process[{0}] has exited.",
 87                             plist[j].Id);
 88                         plist[j] = null;
 89                         i--;    // decrement the process watch count
 90                     }
 91                     else
 92                     {
 93                         Thread.Sleep(250);
 94                     }
 95                 }
 96             }
 97         }
 98         Console.WriteLine("
Client processes finished, exiting.");
 99     }
100 }
101 
102 // Defines the data protocol for reading and writing strings on our stream
103 public class StreamString
104 {
105     private Stream ioStream;
106     private UnicodeEncoding streamEncoding;
107 
108     public StreamString(Stream ioStream)
109     {
110         this.ioStream = ioStream;
111         streamEncoding = new UnicodeEncoding();
112     }
113 
114     public string ReadString()
115     {
116         int len;
117         len = ioStream.ReadByte() * 256;
118         len += ioStream.ReadByte();
119         byte[] inBuffer = new byte[len];
120         ioStream.Read(inBuffer, 0, len);
121 
122         return streamEncoding.GetString(inBuffer);
123     }
124 
125     public int WriteString(string outString)
126     {
127         byte[] outBuffer = streamEncoding.GetBytes(outString);
128         int len = outBuffer.Length;
129         if (len > UInt16.MaxValue)
130         {
131             len = (int)UInt16.MaxValue;
132         }
133         ioStream.WriteByte((byte)(len / 256));
134         ioStream.WriteByte((byte)(len & 255));
135         ioStream.Write(outBuffer, 0, len);
136         ioStream.Flush();
137 
138         return outBuffer.Length + 2;
139     }
140 }
View Code

可靠编程

  因为此示例中的客户端进程和服务器进程可以在同一台计算机上运行,所以提供给 NamedPipeClientStream 对象的服务器名称为 "."。 如果客户端进程和服务器进程在不同的计算机上运行,"." 会被替换为运行服务器进程的计算机的网络名称。

原文地址:https://www.cnblogs.com/amytal/p/11722787.html