鑑於小哥哥、小姐姐們天天的工做壓力都很大。決定之後每一篇文章講解的知識點最多不超過三個。這樣有三個好處安全
進程間傳遞數據,常見的有如下幾種方式:服務器
Socket
:使用套接字在不一樣的進程間通訊,這種通訊方式下,須要佔用系統至少一個端口SendMessage
:經過窗口句柄的方式來通訊,此通訊方式基於 Windows
消息 WM_COPYDATA
來實現Msmq
。但在實際項目中,通常使用 ActiveMQ
、Kafka
、RocketMQ
、RabbitMQ
等這些針對特定場景優化的消息中間件,以得到最大的性能或可伸縮性優點其中,管道、內存映射文件、SendMessage
的方式,通常用於單機上進程間的通訊,在單機上使用這三種方式,比使用 Socket
要相對高效,且更容易控制網絡
而 Socket
、消息隊列或其餘基於Socket
的通訊方式,則適用範圍更廣。它不只適用於本機進程間的通訊,還適用於跨機器(包括跨網段)之間的通訊,好比同一個集羣裏面不一樣服務器之間的通訊、微服務羣下各個微服務之間的通訊。這也是目前用得最多得方式框架
雖然在互聯網化的今天,本機進程間通訊可能用得很少。但在這篇文章中,咱們仍是有必要了解基於管道的進程間通訊方式,後面咱們會目前用得比較普遍的一些框架分佈式
命名管道,它能夠在管道服務器和一個或多個管道客戶端之間提供進程間通訊。其特色以下ide
PipeTransmissionMode.Message
選項),並容許多個客戶端使用相同的管道名稱同時鏈接到服務器端進程它既可用於本機進程間的通訊,也能用於跨機器之間的通訊,但實際中不多這樣用函數
跨機器通訊,特別是在跨網絡的狀況下,目前廣泛的作法是使用一些通訊框架(好比分佈式或微服務中,可以使用Netty
,RPC
、REST
或Thrift
等等),畢竟這些通訊框架大都成熟穩定,還經歷過商用的考驗微服務
用於發送數據的示例代碼以下性能
using System;
using System.IO;
using System.IO.Pipes;
namespace App {
class Program {
static void Main(string[] args) {
/// 第一個參數爲管道的名稱,第二個參數表示此處的管道用於發送數據
using (NamedPipeServerStream pipeServer = new NamedPipeServerStream("pipe_demo", PipeDirection.Out)) {
// 等待鏈接,程序會阻塞在此處,直到有一個鏈接到達
pipeServer.WaitForConnection();
try {
using (StreamWriter sw = new StreamWriter(pipeServer)) {
sw.AutoFlush = true;
// 向鏈接的客戶端發送消息
sw.WriteLine("hello world ");
}
} catch (IOException e) {
Console.WriteLine("ERROR: {0}", e.Message);
}
}
Console.ReadLine();
}
}
}
複製代碼
用於接收數據的示例代碼以下學習
using System;
using System.IO;
using System.IO.Pipes;
namespace App {
class Program {
static void Main(string[] args) {
/// 第一個參數:"." 表示此管道用於本機。此處用 "localhost"、"127.0.0.1" 也是能夠的
/// 第二個參數:管道的名稱
/// 第三個參數:表示此處的管道用於接收數據
using (NamedPipeClientStream pipeClient = new NamedPipeClientStream(".", "pipe_demo", PipeDirection.In)) {
pipeClient.Connect();
using (StreamReader sr = new StreamReader(pipeClient)) {
string tmp;
while ((tmp = sr.ReadLine()) != null) {
Console.WriteLine($"收到數據: {tmp}");
}
}
}
Console.ReadLine();
}
}
}
複製代碼
關於命名管道的命名,咱們這兒使用的是 "pipe_demo"
, 推薦採用 "公司名.項目名稱.模塊名稱.管道用途
" 的方式命名。這不但能夠減少與其餘命名管道名稱衝突的可能性,還可讓這個管道更具備識別性(經過名稱就能指定這個管道是幹嗎的)
其中,經過在建立管道時,指定 PipeDirection
選項,可讓管道工做於雙工、半雙工的通訊模式下
public enum PipeDirection {
// 表示此管道用於接收數據
In = 1,
// 表示此管道用於發送數據
Out = 2,
// 表示此管道既可發送數據,也能夠接收數據
InOut = 3
}
複製代碼
若是對這種通訊方式感興趣,能夠參考 NamedPipeServerStream
與 NamedPipeClientStream
其餘的構造函數,來找到更加符合自身業務的模式
匿名管道只能在本機上提供進程間通訊。與命名管道相比,其有以下特色
服務端 AnonymousPipeServerStream
定義以下
public sealed class AnonymousPipeServerStream : PipeStream {
public AnonymousPipeServerStream();
public AnonymousPipeServerStream(PipeDirection direction);
public AnonymousPipeServerStream(PipeDirection direction, HandleInheritability inheritability);
public AnonymousPipeServerStream(PipeDirection direction, HandleInheritability inheritability, int bufferSize);
public AnonymousPipeServerStream(PipeDirection direction, SafePipeHandle serverSafePipeHandle, SafePipeHandle clientSafePipeHandle);
public AnonymousPipeServerStream(PipeDirection direction, HandleInheritability inheritability, int bufferSize, PipeSecurity pipeSecurity);
public SafePipeHandle ClientSafePipeHandle { get; }
// 此管道的傳輸模式:在匿名管道中,只支持 PipeTransmissionMode.Byte 這種方式
public override PipeTransmissionMode TransmissionMode { get; }
public override PipeTransmissionMode ReadMode { set; }
public void DisposeLocalCopyOfClientHandle();
public string GetClientHandleAsString();
protected override void Dispose(bool disposing);
}
複製代碼
能夠看到,其定義了多個構造函數,提供了本機進程中的多種管道通訊模式。其中
HandleInheritability
用於指明子進程是否能夠繼承服務器端的底層句柄SafePipeHandle
用於指定客戶端和服務端的安全句柄PipeSecurity
用於指定客戶端的訪問權限通常狀況下,咱們只須要使用前三個構造函數便可,後面幾個用的不多
客戶端 AnonymousPipeClientStream
定義以下
public sealed class AnonymousPipeClientStream : PipeStream {
public AnonymousPipeClientStream(string pipeHandleAsString);
public AnonymousPipeClientStream(PipeDirection direction, string pipeHandleAsString);
public AnonymousPipeClientStream(PipeDirection direction, SafePipeHandle safePipeHandle);
// 此管道的傳輸模式:在匿名管道中,只支持 PipeTransmissionMode.Byte 這種方式
public override PipeTransmissionMode TransmissionMode { get; }
public override PipeTransmissionMode ReadMode { set; }
}
複製代碼
其中,pipeHandleAsString
參數是父進程在建立此子進程的時候傳遞的安全句柄
其服務端示例以下
using System;
using System.Diagnostics;
using System.IO;
using System.IO.Pipes;
namespace App {
class Program {
static void Main(string[] args) {
Process pipeClient = new Process();
// 客戶端可執行文件的路徑
pipeClient.StartInfo.FileName = @"C:\Users\Jame\source\repos\ConsoleApp4\ConsoleApp4\bin\Debug\ConsoleApp4.exe";
using (AnonymousPipeServerStream pipeServer = new AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)) {
// 將句柄傳入
pipeClient.StartInfo.Arguments =pipeServer.GetClientHandleAsString();
pipeClient.StartInfo.UseShellExecute = false;
pipeClient.Start();
pipeServer.DisposeLocalCopyOfClientHandle();
try {
using (StreamWriter sw = new StreamWriter(pipeServer)) {
sw.AutoFlush = true;
sw.WriteLine("SYNC");
pipeServer.WaitForPipeDrain();
Console.Write("[SERVER] Enter text: ");
sw.WriteLine(Console.ReadLine());
}
} catch (IOException e) {
Console.WriteLine("[SERVER] Error: {0}", e.Message);
}
}
pipeClient.WaitForExit();
pipeClient.Close();
Console.WriteLine("[SERVER] Client quit. Server terminating.");
Console.ReadLine();
}
}
}
複製代碼
客戶端代碼以下
using System;
using System.IO;
using System.IO.Pipes;
namespace App {
class Program {
static void Main(string[] args) {
if (args.Length > 0) {
// 其中,args[0] 表示傳入的句柄
using (PipeStream pipeClient = new AnonymousPipeClientStream(PipeDirection.In, args[0])) {
using (StreamReader sr = new StreamReader(pipeClient)) {
string temp;
do {
Console.WriteLine("[CLIENT] Wait for sync...");
temp = sr.ReadLine();
}
while (!temp.StartsWith("SYNC"));
while ((temp = sr.ReadLine()) != null) {
Console.WriteLine("[CLIENT] Echo: " + temp);
}
}
}
}
Console.ReadLine();
}
}
}
複製代碼
在匿名管道這個例子中,須要咱們先編譯客戶端的代碼,不然可能會有錯誤
經過以上的講解,若是須要使用管道來實現進程間的通訊,咱們能夠按如下方式選擇