System.IO之使用管道在进程间通信 (System.IO.Pipes使用)

System.IO之使用管道在进程间通信 (System.IO.Pipes使用)

管道的用途是在同一台机器上的进程之间通信,也可以在同一网络不同机器间通信。在.Net中可以使用匿名管道和命名管道。管道相关的类在System.IO.Pipes命名空间中。.Net中管道的本质是对windows API中管道相关函数的封装。

使用匿名管道在父子进程之间通信:

匿名管道是一种半双工通信,所谓的半双工通信是指通信的两端只有一端可写另一端可读;匿名管道只能在同一台机器上使用,不能在不同机器上跨网络使用。

匿名管道顾名思义就是没有命名的管道,它常用于父子进程之间的通信,父进程在创建子进程是要将匿名管道的句柄作为字符串传递给子进程,看下例子:

父进程创建了一个AnonymousPipeServerStream,然后启动子进程,并将创建的AnonymousPipeServerStream的句柄作为参数传递给子进程。如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
Process process =  new  Process();
process.StartInfo.FileName =  "child.exe" ;
  //创建匿名管道流实例,
using  (AnonymousPipeServerStream pipeStream =
     new  AnonymousPipeServerStream(PipeDirection.Out, HandleInheritability.Inheritable)) {
  //将句柄传递给子进程
     process.StartInfo.Arguments = pipeStream.GetClientHandleAsString();
     process.StartInfo.UseShellExecute =  false ;
     process.Start();
  //销毁子进程的客户端句柄?
     pipeStream.DisposeLocalCopyOfClientHandle();
  
     using  (StreamWriter sw =  new  StreamWriter(pipeStream)) {
         sw.AutoFlush =  true ;
//向匿名管道中写入内容
         sw.WriteLine(Console.ReadLine());
     }
}
  
process.WaitForExit();
process.Close();

子进程声明了一个AnonymousPipeClientStream实例,并从此实例中读取内容,如下代码:

1
2
3
4
5
6
7
using  (StreamReader sr =  new  StreamReader(
     new  AnonymousPipeClientStream(PipeDirection.In, args[0]))) {
     string  line;
     while  ((line = sr.ReadLine()) !=  null ) {
         Console.WriteLine( "Echo: {0}" , line);
     }
}

这个程序要在cmd命令行中执行,否则看不到执行效果,执行的结果是在父进程中输入一行文本,子进程输出Echo:文本。

命名管道:

命名管道的功能比匿名管道更强大,可以在进程之间做双工通信(即通信的两个进程都是既可以读也可写的);命名管道也可以实现跨网络在不同机器之间进行通信。可以在多线程中创建多个NamedPipeServerStream实例,为多个client端服务。另外命名管道还支持消息传输,这样client端可以读取任意长度的消息,而无须知道消息的长度。

如下服务端进程代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
using  (NamedPipeServerStream pipeStream =  new  NamedPipeServerStream( "testpipe" ))
{
     pipeStream.WaitForConnection();
 
     using  (StreamWriter writer =  new  StreamWriter(pipeStream))
     {
         writer.AutoFlush =  true ;
         string  temp;
 
         while  ((temp = Console.ReadLine()) !=  "stop" ) {
             writer.WriteLine(temp);
         }
     }
}

如下客户端进程代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static  void  Main( string [] args)
{
     using  (NamedPipeClientStream pipeStream =  new  NamedPipeClientStream( "testpipe" ))
     {
         pipeStream.Connect();
         //在client读取server端写的数据
         using  (StreamReader rdr =  new  StreamReader(pipeStream))
         {
             string  temp;
             while  ((temp = rdr.ReadLine()) !=  "stop" )
             {
                 Console.WriteLine( "{0}:{1}" ,DateTime.Now,temp);
             }
         }
     }
 
     Console.Read();
}

上面代码执行结果是服务端输入文本,客户端显示接受到文本的时间和文本内容。上面说了命名管道是全双工通信的,所以你也可以让客户端写内容,而服务端接受内容,代码大同小异。

基于消息传输的命名管道:

基于消息的命名管道可以传递不定长的内容,而无需传递内容长度或者结束符,上面非基于消息的传输我们都是在向管道中输入一段文本,使用WriteLine方法以回车换行作为结束符传输信息,而管道的另一端再使用ReadLine方法以读取到回车换行符作为一个消息传递结束的标志;而在使用基于消息传输时就不必这么做了。如下示例:

服务端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
static  void  Main( string [] args)
{
     UTF8Encoding encoding =  new  UTF8Encoding();
     string  message1 =  "Named Pipe Message Example." ;
     string  message2 =  "Another Named Pipe Message Example." ;
     Byte[] bytes;
     using  (NamedPipeServerStream pipeStream =  new
             NamedPipeServerStream( "messagepipe" , PipeDirection.InOut, 1,
             PipeTransmissionMode.Message, PipeOptions.None))
     {
         pipeStream.WaitForConnection();
 
         // Let’s send two messages.
         bytes = encoding.GetBytes(message1);
         pipeStream.Write(bytes, 0, bytes.Length);
         bytes = encoding.GetBytes(message2);
         pipeStream.Write(bytes, 0, bytes.Length);
 
     }
}

客户端代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
static  void  Main( string [] args)
{
     Decoder decoder = Encoding.UTF8.GetDecoder();
     Byte[] bytes =  new  Byte[10];
     Char[] chars =  new  Char[10];
     using  (NamedPipeClientStream pipeStream =
             new  NamedPipeClientStream( "messagepipe" ))
     {
         pipeStream.Connect();
         pipeStream.ReadMode = PipeTransmissionMode.Message;
         int  numBytes;
         do
         {
             string  message =  "" ;
 
             do
             {
                 numBytes = pipeStream.Read(bytes, 0, bytes.Length);
                 int  numChars = decoder.GetChars(bytes, 0, numBytes, chars, 0);
                 message +=  new  String(chars, 0, numChars);
             while  (!pipeStream.IsMessageComplete);
 
             decoder.Reset();
             Console.WriteLine(message);
         while  (numBytes != 0);
     }
}

基于消息传递信息需要在构造NamedPipeServerStream时指定PipeTransmissionMode为PipeTransmissionMode.Message;而在客户端接收时也需指定NamedPipeClientStream的ReadMode属性为PipeTransmissionMode.Message

以上只是管道使用的简单实例,真正使用时还需要考虑多线程访问控制,权限控制等。

本文内容翻译自:http://blogs.msdn.com/b/bclteam/archive/2006/12/07/introducing-pipes-justin-van-patten.aspx 根据我自己看法对原文做了一些改动。

源码下载

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值