創建命名管道
命名管道常常用於應用程序之間的通迅,由於不需要進行序列化和反序列化操作,效率是非常高的。相比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("\r\n\r\n")) break; } else { if (str == "") sb.AppendLine(); else sb.AppendLine(str); } } strAll = strAll.Substring(0, strAll.Length - "\r\n\r\n\r\n".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 = "fff\r\ndddd\r\n"; 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下載