使用管道通信
为了 在线程和进程之间通信,在不同的系统之间 快速通信,可以使用管道
.NET中,管道实现为流。因此
- 可以把字节发送到管道
- 可以使用流的所有特性,例如
- 使用 读取器 和 写入器
- 创建
CryptoStream或GZipStream把加密或压缩的数据写入管道
管道实现为不同的类型:
- 命名管道
- 名称 可以用于连接到 每一端
- 匿名管道
- 不能用于 不同系统 之间的通信
- 只能用于 一个父子进程 之间的通信 或 不同任务 之间的通信
- 只能是单向的
示例:使用命名管道 在不同的进程之间通信
- 一个控制台应用程序 充当服务器,从管道中读取数据
- 另一个控制台应用程序 把消息写入管道
命名管道 服务器端:读取者
NamedPipeServerStream派生自基类PipeStream。PipeStream派生自基类Stream- 第一个参数:管道名称 (多个进程可以使用该管道)
- 第二个参数:管道的方向
- 读:
PipeDirection.In - 写:
PipeDirection.Out - 读写:
PipeDirection.InOut
- 读:
NamedPipeServerStream调用WaitForConnection()等待写入方连接NamedPipeServerStream调用Read()把消息读入缓冲区字节数组- 命名管道的其他配置:
PipeTransmissionMode设置为Byte: 发送一个连续的流Message: 检索每条消息
WriteThrough立即写入管道,不缓存- 可以 为输入输出 配置 缓冲区大小
- 可以 配置 管道安全性。指定 谁允许 读写管道
- 可以 配置 管道句柄的 可继承性。这对 子进程进行通信 是很重要的
private static void PipesReader(string pipeName)
{
try
{
using (var pipeReader = new NamedPipeServerStream(pipeName, PipeDirection.In))
{
pipeReader.WaitForConnection();
Console.WriteLine("reader connected");
const int BUFFERSIZE = 256;
bool completed = false;
while (!completed)
{
byte[] buffer = new byte[BUFFERSIZE];
int nRead = pipeReader.Read(buffer, 0, BUFFERSIZE);
string line = Encoding.UTF8.GetString(buffer, 0, nRead);
Console.WriteLine(line);
if (line == "bye") completed = true;
}
}
Console.WriteLine("completed reading");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
还可以 使用
StreamReader而不是读取字节数组
private static void PipesReader2(string pipeName)
{
try
{
var pipeReader = new NamedPipeServerStream(pipeName, PipeDirection.In);
using (var reader = new StreamReader(pipeReader))
{
pipeReader.WaitForConnection();
Console.WriteLine("reader connected");
bool completed = false;
while (!completed)
{
string line = reader.ReadLine();
Console.WriteLine(line);
if (line == "bye") completed = true;
}
}
Console.WriteLine("completed reading");
Console.ReadLine();
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
命名管道 客户端:写入者
NamedPipeClientStream- 第一个参数:服务器名称 (因为命名管道 可以在网络上通信)
- 第二个参数:管道的名称
- 第三个参数:管道的方向
NamedPipeClientStream调用Connect()来连接NamedPipeClientStream调用Write()来写入
private static void PipesWriter(string serverName, string pipeName)
{
try
{
using (var pipeWriter = new NamedPipeClientStream(serverName, pipeName, PipeDirection.Out))
{
pipeWriter.Connect();
Console.WriteLine("writer connected");
bool completed = false;
while (!completed)
{
string input = Console.ReadLine();
if (input == "bye") completed = true;
byte[] buffer = Encoding.UTF8.GetBytes(input);
pipeWriter.Write(buffer, 0, buffer.Length);
}
}
Console.WriteLine("completed writing");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
还可以 使用
StreamWriter而不是读取字节数组
StreamWriter调用WriteLine,把消息发送给服务器- 默认情况下,消息不立即发送,而是缓存起来
- 调用
Flush()方法,把消息推到服务器上 - 也可以立即传送所有消息,这需要配置
private static void PipesWriter2(string serverName, string pipeName)
{
var pipeWriter = new NamedPipeClientStream(serverName, pipeName, PipeDirection.Out);
using (var writer = new StreamWriter(pipeWriter))
{
pipeWriter.Connect();
Console.WriteLine("writer connected");
bool completed = false;
while (!completed)
{
string input = Console.ReadLine();
if (input == "bye") completed = true;
writer.WriteLine(input);
writer.Flush();
}
}
Console.WriteLine("completed writing");
}
使用匿名管道
AnonymousPipeServerStream的构造函数:
- 第一个参数:枚举
PipeDirection.In, 服务器端 充当 读取器 - 第二个参数:枚举
HandleInheritability.None
通信的另一端 需要知道 管道的 客户端句柄
- 这个 客户端句柄 通过调用
AnonymousPipeServerStream的GetClientHandleAsString()方法得到,并转换成一个字符串 - 将这个字符串 赋予一个变量
- 这个变量,以后由充当写入器的 客户端使用
AnonymousPipeClientStream的构造函数:
- 第一个参数:枚举
PipeDirection.In, 客户端 充当 写入器 - 第二个参数:管道的 客户端句柄
- 这里,
ManualResetEventSlim是为了在管道的创建时机发出信号
两个任务 用匿名管道 通信
public class Program
{
private string _pipeHandle;
private ManualResetEventSlim _pipeHandleSet;
public static void Main()
{
var p = new Program();
p.Run();
Console.ReadLine();
}
public void Run()
{
_pipeHandleSet = new ManualResetEventSlim(initialState: false);
Task.Run(() => Reader());
Task.Run(() => Writer());
}
private void Writer()
{
Console.WriteLine("anonymous pipe writer");
_pipeHandleSet.Wait();
var pipeWriter = new AnonymousPipeClientStream(PipeDirection.Out, _pipeHandle);
using (var writer = new StreamWriter(pipeWriter))
{
writer.AutoFlush = true;
Console.WriteLine("starting writer");
for (int i = 0; i < 5; i++)
{
writer.WriteLine($"Message {i}");
Task.Delay(500).Wait();
}
writer.WriteLine("end");
}
}
private void Reader()
{
try
{
Console.WriteLine("anonymous pipe reader");
var pipeReader = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.None);
using (var reader = new StreamReader(pipeReader))
{
_pipeHandle = pipeReader.GetClientHandleAsString();
Console.WriteLine($"pipe handle: {_pipeHandle}");
_pipeHandleSet.Set();
bool end = false;
while (!end)
{
string line = reader.ReadLine();
Console.WriteLine(line);
if (line == "end") end = true;
}
Console.WriteLine("finished reading");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
另一个 匿名管道的 示例
private static void AnonymousReader()
{
using (var reader = new AnonymousPipeServerStream(PipeDirection.In, HandleInheritability.Inheritable))
{
Console.WriteLine("using anonymous pipe");
string pipeHandle = reader.GetClientHandleAsString();
Console.WriteLine($"pipe handle: {pipeHandle}");
byte[] buffer = new byte[256];
int nRead = reader.Read(buffer, 0, 256);
string line = Encoding.UTF8.GetString(buffer, 0, 256);
Console.WriteLine(line);
}
}
private static void AnonymousWriter()
{
Console.WriteLine("using anonymous pipe");
Console.Write("pipe handle: ");
string pipeHandle = Console.ReadLine();
using (var pipeWriter = new AnonymousPipeClientStream(PipeDirection.Out, pipeHandle))
using (var writer = new StreamWriter(pipeWriter))
{
for (int i = 0; i < 100; i++)
{
writer.WriteLine($"Message {i}");
Task.Delay(500).Wait();
}
}
}