使用命名管道實現程序間通訊
建立命名管道
命名管道常常用於應用程式之間的通迅,由於不需要進行序列化和反序列化操作,效率是非常高的。相比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下載