.net 命名管道(NamedPipe) 的使用

命名管道常常用于应用程序之间的通迅,由于不需要进行序列化和反序列化操作,效率还是非常高的。我们今天做个演示看看。

类似于TCP/IP的模式的c/s结构。我们先建立一个服务器端:

           using (NamedPipeServerStream pipeServer =
                
new NamedPipeServerStream("testpipe", PipeDirection.Out))//创建连接
            {
                pipeServer.WaitForConnection();//等待连接,程序会阻塞在此处,直到有一个连接到达

                
try
                {
                    
// Read user input and send that to the client process.
                    using (StreamWriter sw = new StreamWriter(pipeServer))
                    {
                        sw.AutoFlush 
= true;
                        sw.WriteLine(
"hello world ");
                    }
                }
                
// Catch the IOException that is raised if the pipe is broken
                
// or disconnected.
                catch (IOException e)
                {
                    Console.WriteLine(
"ERROR: {0}", e.Message);
                }
            }

上面的代码很简单,构建NamedPipeServerStream对象,然后调用该对象的WaitForConnection方法等待客户端的连接,一旦连接建立,向客户端写入一个字符串“hello world 

下面写一个客户端。

            using (NamedPipeClientStream pipeClient =
                        
new NamedPipeClientStream(".""testpipe", PipeDirection.In))
            {
                pipeClient.Connect();

                
using (StreamReader sr = new StreamReader(pipeClient))
                {
                    
string temp;
                    
while ((temp = sr.ReadLine()) != null)
                    {
                        MessageBox.Show(
string.Format("Received from server: {0}", temp));
                    }
                }
            }

 客户端构建了一个NamedPipeClientStream 对象,调用Connect方法建立连接,连接通畅后,立即从通道里读取数据,这里会读取到"hello world "字符。

本文结束。太简单了吧。微软都把这些类库封装的很方便了。

---------------------

以下是扩充内容。

既然 命名管道 给我们提供一种通讯方式,那么我们尝试使用这个通讯做个 C/S 结构的消息监听演示。我们利用 命名管道 封装两个类,一个服务类,一个客户机类。使得我们在使用的时候无需考虑通讯的底层实现。

那么我们先看下服务端的类:

/* ----------------------------------------------------------
* @名称    :    
* @描述    :    
* @创建人  :    张云飞
* @创建日期:    2011/7/12 9:57:55
* @修改记录:
* ----------------------------------------------------------
*/


using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.IO.Pipes;
using System.IO;
using System.Threading;

namespace SimpleCode.NamedPipeLib
{
    
public class NamedPipeListenServer
    {
        List
<NamedPipeServerStream> _serverPool = new List<NamedPipeServerStream>();
        
string _pipName = "test";
        
public NamedPipeListenServer(string pipName)
        {
            _pipName 
= pipName;
        }

        
/// <summary>
        
/// 创建一个NamedPipeServerStream
        
/// </summary>
        
/// <returns></returns>
        protected NamedPipeServerStream CreateNamedPipeServerStream()
        {
            NamedPipeServerStream npss 
= new NamedPipeServerStream(_pipName, PipeDirection.InOut, 10);
            _serverPool.Add(npss);
            Console.WriteLine(
"启动了一个NamedPipeServerStream " + npss.GetHashCode());
            
return npss;
        }

        
/// <summary>
        
/// 销毁
        
/// </summary>
        
/// <param name="npss"></param>
        protected void DistroyObject(NamedPipeServerStream npss)
        {
            npss.Close();
            
if (_serverPool.Contains(npss))
            {
                _serverPool.Remove(npss);
            }
            Console.WriteLine(
"销毁一个NamedPipeServerStream " + npss.GetHashCode());
        }

        
public void Run()
        {
            
using (NamedPipeServerStream pipeServer = CreateNamedPipeServerStream())
            {
                pipeServer.WaitForConnection();
                Console.WriteLine(
"建立一个连接 " + pipeServer.GetHashCode());

                Action act 
= new Action(Run);
                act.BeginInvoke(
nullnull);

                
try
                {
                    
bool isRun = true;
                    
while (isRun)
                    {
                        
string str = null;
                        StreamReader sr 
= new StreamReader(pipeServer);
                        
while (pipeServer.CanRead && (null != (str = sr.ReadLine())))
                        {
                            ProcessMessage(str, pipeServer);

                            
if (!pipeServer.IsConnected)
                            {
                                isRun 
= false;
                                
break;
                            }
                        }

                        Thread.Sleep(
50);
                    }
                }
                
// Catch the IOException that is raised if the pipe is broken
                
// or disconnected.
                catch (IOException e)
                {
                    Console.WriteLine(
"ERROR: {0}", e.Message);
                }
                
finally
                {
                    DistroyObject(pipeServer);
                }
            }

        }

        
/// <summary>
        
/// 处理消息
        
/// </summary>
        
/// <param name="str"></param>
        
/// <param name="pipeServer"></param>
        protected virtual void ProcessMessage(string str, NamedPipeServerStream pipeServer)
        {
            
// Read user input and send that to the client process.
            using (StreamWriter sw = new StreamWriter(pipeServer))
            {
                sw.AutoFlush 
= true;
                sw.WriteLine(
"hello world " + str);
            }
        }

        
/// <summary>
        
/// 停止
        
/// </summary>
        public void Stop()
        {
            
for (int i = 0; i < _serverPool.Count; i++)
            {
                var item 
= _serverPool[i];

                DistroyObject(item);
            }
        }
    }
}

我们使用 List<NamedPipeServerStream> _serverPool 做为一个集合容器,记录和保存所有的建立的连接。在Run方法里创建新的 命名管道连接 的实例。注意我们这里使用了一个异步委托。

                Action act = new Action(Run);
                act.BeginInvoke(
nullnull);
在一个Run方法执行时,异步执行了另一个Run方法,在这个Run方法的实现了阻塞监听,也就是一个新的连接建立后,立即建立一个新的监听连接,使得在处理一个连接的数据时,也可以等待新的客户端连接进入。

Stop方法会遍历所有的连接,保证销毁所有的连接。 ProcessMessage方法是处理客户机发来的消息,我们这里使用了ReadLine方法,也就是我们认为一行作为一个消息的单元。注意这个方法是virtual的,我们可以实现它的重载,以增加新的消息处理方式。

下面是客户端的封装类:

/* ----------------------------------------------------------
* @名称    :    
* @描述    :    
* @创建人  :    张云飞
* @创建日期:    2011/7/12 11:31:30
* @修改记录:
* ----------------------------------------------------------
*/

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO.Pipes;
using System.IO;

namespace SimpleCode.NamedPipeLib
{
    
public class NamedPipeClient : IDisposable
    {
        
string _serverName;
        
string _pipName;
        NamedPipeClientStream _pipeClient;

        
/// <summary>
        
/// 
        
/// </summary>
        
/// <param name="serverName">服务器地址</param>
        
/// <param name="pipName">管道名称</param>
        public NamedPipeClient(string serverName, string pipName)
        {
            _serverName 
= serverName;
            _pipName 
= pipName;

            _pipeClient 
= new NamedPipeClientStream(serverName, pipName, PipeDirection.InOut);

        }

        
/// <summary>
        
/// 查询
        
/// </summary>
        
/// <param name="request"></param>
        
/// <returns></returns>
        public string Query(string request)
        {
            
if (!_pipeClient.IsConnected)
            {
                _pipeClient.Connect(
10000);
            }

            StreamWriter sw 
= new StreamWriter(_pipeClient);
            sw.WriteLine(request);
            sw.Flush();

            StreamReader sr 
= new StreamReader(_pipeClient);
            
string temp;
            
string returnVal = "";
            
while ((temp = sr.ReadLine()) != null)
            {
                returnVal 
= temp;
                
//nothing
            }
            
return returnVal;
        }

        
#region IDisposable 成员

        
bool _disposed = false;
        
public void Dispose()
        {
            
if (!_disposed && _pipeClient != null)
            {
                _pipeClient.Dispose();
                _disposed 
= true;
            }
        }

        
#endregion
    }
}

这个类需要注意的是,在构造里构造类命名管道的对象。有意思的地方是Query方法,该方法会接受一个string参数,并且返回一个string对象,实际会将输入参数的内容发送往服务端,将服务端的处理结果做为该方法的返回值。

服务端使用演示:

            NamedPipeListenServer svr = new NamedPipeListenServer("test");
            svr.Run();

客户端使用演示:

            using (NamedPipeClient client = new NamedPipeClient(".","test"))
            {
                MessageBox.Show( client.Query(
"fff"));
                MessageBox.Show(client.Query(
"54353"));
            }

呵呵,使用起来更简单和方便了。而且完全不用考虑内部的命名管道连接。

本文完。

完整的代码下载

原文地址:https://www.cnblogs.com/vir56k/p/2103994.html