One 、 Preface :
In project development, we often encounter , The need for communication calls between multiple processes .
The common ways are :
- The Conduit : Including named pipes and anonymous pipes
- Memory mapped files : With the help of the mapping relationship between files and memory space , application ( Including multiple processes ) It can directly perform read and write operations on memory , So as to realize inter process communication
Socket
: Use sockets to communicate between different processes , In this way , You need to occupy at least one port of the systemSendMessage
: Communicate through the window handle , This communication method is based onWindows
newsWM_COPYDATA
To achieve- Message queue : In the case of low performance requirements , We can use
Msmq
. But in the real world , Usually useActiveMQ
、Kafka
、RocketMQ
、RabbitMQ
Wait for these message oriented middleware optimized for specific scenarios , For maximum performance or scalability benefits
among , The Conduit 、 Memory mapped files 、SendMessage
The way , It is usually used for communication between processes on a single machine , Use these three methods on a single machine , Than using Socket
Be relatively efficient , And it is easier to control the network .
This article will introduce the use of .Net Pipeline mode realizes multi process communication .
Two 、 The Conduit
Pipelines provide a platform for interprocess communication . There are two types of pipes :
- Anonymous pipeline :
Anonymous pipes provide interprocess communication on the local computer . Compared with pipe naming , Although anonymous pipes require less overhead , But the services provided are limited .
Anonymous pipes are one-way , Cannot be used through the network . Only one server instance is supported .
Anonymous pipes can be used for inter thread communication , It can also be used for communication between parent and child processes , Because the pipeline handle can be easily passed to the child process created .
- name pipes :
Named pipes provide interprocess communication between a pipeline server and one or more pipeline clients .
Named pipes can be unidirectional , It can also be two-way . They support message based communication , It also allows multiple clients to connect to the server process at the same time with the same pipe name .
Named pipes also support simulation , In this way, the connection process can use its own permissions on the remote server .
3、 ... and 、 application
1、 Anonymous pipeline : Realization effect : The server passes the input content to the client for output .
- Server side : Create an output anonymous pipeline , Pipe console input to child processes
using System; using System.Diagnostics; using System.IO; using System.IO.Pipes; namespace IpcServer { class AonymousPipeServer { public static void Run() { // Client process Process pipeClient = new Process(); pipeClient.StartInfo.FileName = "IpcClient.exe"; // Create an output anonymous pipeline 、 Specifies that the handle is inherited by the child process using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)) { Console.WriteLine("[SERVER] Pipeline transmission mode : {0}.", pipeServer.TransmissionMode); // Pass the handle of the client process to the server . pipeClient.StartInfo.Arguments = pipeServer.GetClientHandleAsString(); pipeClient.StartInfo.UseShellExecute = false; pipeClient.Start(); pipeServer.DisposeLocalCopyOfClientHandle(); try { // Read the input content of the server , Send to client process using (StreamWriter sw = new StreamWriter(pipeServer)) { sw.AutoFlush = true; // send out “ Sync message ” And wait for the client to receive . sw.WriteLine("SYNC"); // Wait for the client to read all content pipeServer.WaitForPipeDrain(); // Send console data to child process Console.Write("[SERVER] Input text : "); sw.WriteLine(Console.ReadLine()); } } catch (IOException e) { Console.WriteLine("[SERVER] abnormal : {0}", e.Message); } } pipeClient.WaitForExit(); pipeClient.Close(); Console.WriteLine("[SERVER] Client exit , The server terminates ."); } } }
- client : Create an input pipe , Get the incoming data from the server from the pipeline and output
using System; using System.IO; using System.IO.Pipes; namespace IpcClient { class AonymousPipeClient { public static void Run(string[] args) { if (args.Length > 0) { // Create an input type anonymous pipeline using (PipeStream pipeClient = new AnonymousPipeClientStream(PipeDirection.In, args[0])) { Console.WriteLine("[CLIENT] Current pipeline transmission mode : {0}.", pipeClient.TransmissionMode); // Create read stream , Read... From the pipe using (StreamReader sr = new StreamReader(pipeClient)) { string temp; // Waiting for the message from the server do { Console.WriteLine("[CLIENT] Synchronization waiting ..."); temp = sr.ReadLine(); } while (!temp.StartsWith("SYNC")); // Read the server data and echo to the console. while ((temp = sr.ReadLine()) != null) { Console.WriteLine("[CLIENT] Respond to : " + temp); } } } } Console.Write("[CLIENT] Any key to exit ..."); Console.ReadLine(); } } }
Effect of the sample :
2、 name pipes :
- Server side : Read the file address sent by the client through the pipeline , Read it in the server and return it to the client
using System; using System.IO; using System.IO.Pipes; using System.Text; using System.Threading; namespace IpcServer { public class NamedPipeServer { private static int numThreads = 4; public static void Run() { int i; Thread[] servers = new Thread[numThreads]; Console.WriteLine("\n*** Named pipe server example ***\n"); Console.WriteLine(" Wait for the client to 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}] end .", servers[j].ManagedThreadId); servers[j] = null; i--; // Reduce the number of threads } } } } Console.WriteLine("\n The server thread has completed , Exiting ."); Console.ReadLine(); } private static void ServerThread(object data) { // Pipe name 、 Pipeline direction NamedPipeServerStream pipeServer = new NamedPipeServerStream("testpipe", PipeDirection.InOut, numThreads); int threadId = Thread.CurrentThread.ManagedThreadId; // Wait for the client to connect pipeServer.WaitForConnection(); Console.WriteLine(" Client connection successful thread[{0}].", threadId); try { // Read the customer's request . After the client writes to the pipeline , Its security token will be available StreamString ss = new StreamString(pipeServer); // Use the string expected by the client to verify our identity to the connected client . ss.WriteString("I am the one true server!"); string filename = ss.ReadString(); // Read the contents of the file when simulating the client . ReadFileToStream fileReader = new ReadFileToStream(ss, filename); Console.WriteLine(" Read the file : {0} Threads [{1}] Pipeline user : {2}.", filename, threadId, pipeServer.GetImpersonationUserName()); pipeServer.RunAsClient(fileReader.Start); } catch (IOException e) { Console.WriteLine(" abnormal : {0}", e.Message); } pipeServer.Close(); } } // Define the data protocol used to read and write strings on the 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 methods executed in the context of impersonating users 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); } } }
- client : After connecting the server named pipe , Send the file path to read ; Read the contents of the file returned by the server and output
using System; using System.Diagnostics; using System.IO; using System.IO.Pipes; using System.Security.Principal; using System.Text; using System.Threading; namespace IpcClient { class NamedPipeClient { private static int numClients = 4; public static void Run(string[] args) { if (args.Length > 0) { if (args[0] == "spawnclient") { //. Representing this machine ( It can be replaced by specific IP) var pipeClient = new NamedPipeClientStream(".", "testpipe", PipeDirection.InOut, PipeOptions.None, TokenImpersonationLevel.Impersonation); Console.WriteLine(" Connect server ...\n"); pipeClient.Connect(); var ss = new StreamString(pipeClient); // Validate the server's signature string. if (ss.ReadString() == "I am the one true server!") { // Send file path , The server returns after reading ss.WriteString(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "namedpipeTxt.txt")); // Output file content Console.Write($" The contents of the document :{ss.ReadString()}\r\n"); } else { Console.WriteLine(" The server failed to verify "); } pipeClient.Close(); Console.ReadLine(); } } else { Console.WriteLine("\n*** Named pipe client example ***\n"); StartClients(); } } // Start client private static void StartClients() { string currentProcessName = Environment.CommandLine; currentProcessName = currentProcessName.Trim('"', ' '); currentProcessName = Path.ChangeExtension(currentProcessName, ".exe"); Process[] plist = new Process[numClients]; Console.WriteLine(" Generate client process ...\n"); if (currentProcessName.Contains(Environment.CurrentDirectory)) { currentProcessName = currentProcessName.Replace(Environment.CurrentDirectory, String.Empty); } // Compatible processing currentProcessName = currentProcessName.Replace("\\", String.Empty); currentProcessName = currentProcessName.Replace("\"", String.Empty); int i; for (i = 0; i < numClients; i++) { // Start the client process , Use the same named pipe 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 been withdrawn from ."); plist[j] = null; i--; } else { Thread.Sleep(250); } } } } Console.WriteLine("\n Client process completed , Exiting "); } } // Define the data protocol used to read and write strings on the 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(); len = len * 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; } } }
Four 、 summary :
If you use pipes to realize the communication between processes , You can choose as follows :
- If you only need one-way communication , And the relationship between the two processes is a parent-child process , Anonymous pipes can be used
- If you need two-way communication , Then use named pipes
- If we can't decide what to choose , Then choose the two-way communication mode of named pipeline . On today's computers , We can ignore the difference between the performance of named pipes and anonymous pipes . And the named pipeline of two-way communication , One way , It can also be bidirectional , More flexible
Source code :IpcService