1. 程式人生 > 其它 >使用命名管道實現程序間通訊

使用命名管道實現程序間通訊

建立命名管道

命名管道常常用於應用程式之間的通迅,由於不需要進行序列化和反序列化操作,效率是非常高的。相比TCP通訊方式,效率更高,但比共享記憶體要低點。 命名管道可以在本地機器或者區域網內機器實現程序間通訊,所以是最佳的通訊方式。

建立一個NamedPipeServerStream:

NamedPipeServerStream pipeServer = new NamedPipeServerStream(_pipName, PipeDirection.InOut, 10);

這裡表示命名管道伺服器的管道放心為雙向通訊,類似於TCP雙工。接著,使用下面的程式碼等待連線:

pipeServer.WaitForConnection();

如果有連線,就可以使用流閱讀器進行閱讀:

 StreamReader sr = new StreamReader(pipeServer);

同樣,也可以使用流寫操作器,將資料寫入流,管道的另一端,可以讀取這個流:

 using (StreamWriter sw = new StreamWriter(pipeServer))
 {
       sw.AutoFlush = true;
       sw.WriteLine("hello world " + str);
 }

注意:此處使用了using,意味著寫完就會關閉流,但同時也會關閉管道,所以需要注意。假如客戶端要讀取全部資料,那麼需要等到這裡關閉流。

自定義應用層通訊協議

如何讀取管道的全部資料,看下面的程式碼:

 StreamReader sr = new StreamReader(pipeServer);
 string text =sr.ReadToEnd();

這種方式可以讀取全部資料,但是,在管道的另外一段,如果留寫操作器不呼叫 Close方法,這裡沒法讀取完成,程式會阻塞在這裡。 所以,必須定義一個“應用協議”,客戶端告訴服務端合適結束讀取資料。

我們仿照HTTP協議的方法,使用連續的2個以上的回車換行表示HTTP頭資訊結束,我們也這樣定義,並附加其它標記來表示流資料傳送完畢,參考傳送端:

 public string Query(string request)
        {
            if (!_pipeClient.IsConnected)
            {
                _pipeClient.Connect(10000);
            }

            StreamWriter sw = new StreamWriter(_pipeClient);
            sw.WriteLine(request);
            sw.WriteLine();//連續2個換行外加"#END"表示結束
            sw.WriteLine();
            sw.WriteLine("#END");
            sw.Flush();

            StreamReader sr = new StreamReader(_pipeClient);
            string returnVal = sr.ReadToEnd();
            return returnVal;
        }

而在服務端,採用下面的方式完成流資料的讀取:

string str = null;
 string strAll = null;
 System.Text.StringBuilder sb = new System.Text.StringBuilder();

 StreamReader sr = new StreamReader(pipeServer);
 while (pipeServer.CanRead && (null != (str = sr.ReadLine())))
 {
     
     //當遇到連續2個換行外加#END,表示輸入結束
     if (str == "#END" )
     {
         strAll = sb.ToString();
         if (strAll.EndsWith("rnrn"))
             break;
     }
     else
     {
         if (str == "")
             sb.AppendLine();
         else
             sb.AppendLine(str);
     }
 }

 strAll = strAll.Substring(0, strAll.Length - "rnrnrn".Length);

測試和下載

最後,寫個客戶端和服務端控制檯程式:

namespace NamePipedSample_Server
{
    class Program
    {
        static void Main(string[] args)
        {
            NamedPipeListenServer svr = new NamedPipeListenServer("test");
            svr.Run();
            Console.Read();
        }
    }
}
namespace NamePipedSample_Client
{
    class Program
    {
        static void Main(string[] args)
        {
            string sendStr = null;
            using (NamedPipeClient client = new NamedPipeClient(".", "test"))
            {
                sendStr = "fffrnddddrn";
                Console.WriteLine("send:{0}",sendStr);
                Console.WriteLine("Reply:{0}",client.Query(sendStr));

                sendStr = "54353";
                Console.WriteLine("send:{0}", sendStr);
                Console.WriteLine("Reply:{0}", client.Query(sendStr));

                sendStr = "aaaaaaa";
                Console.WriteLine("send:{0}", sendStr);
                Console.WriteLine("Reply:{0}", client.Query(sendStr));
            }
            Console.WriteLine("send all ok.");
            Console.Read();
        }
    }
}

 跨機器使用命名管道

上面的程式在本地機器使用沒問題的,但是跨機器可能會遇到問題,在使用的時候,需要將主機名字 "." 替換成 實際的區域網主機名字,例如:

using (NamedPipeClient client = new NamedPipeClient("user-xxxPc", "test"))
{
 //
}

但是這樣可能還是無法訪問,會報下面的錯誤:

“System.IO.IOException”型別的未經處理的異常在 System.Core.dll 中發生 

其他資訊: 登入失敗: 未知的使用者名稱或錯誤密碼。

此時需要在客戶機器上,位址列裡面輸入下面的地址: \user-xxxPc

此時會提示輸入使用者名稱,密碼,最後勾選 “記住賬號”,下次即可使用了。

經過測試,這種方法是先命名管道客戶端-伺服器通訊成功。 本文程式是在網友原來文章的基礎上改進的,在此表示感謝,原文地址:  http://blog.csdn.net/educast/article/details/7219774

本文程式Demo下載