.NET中應(yīng)用程序內(nèi)共享UdpClient聯(lián)機(jī)的實(shí)現(xiàn)方法
原始碼下載: MutualUdpClientSample_jb51net.rar
在開發(fā)與遠(yuǎn)程設(shè)備通訊的系統(tǒng)時(shí),為了提高數(shù)據(jù)傳輸?shù)男剩3?huì)選擇UDP這個(gè)通訊協(xié)議來(lái)作為數(shù)據(jù)傳輸?shù)拿浇?。?.NET framework中所提供的UdpClient對(duì)象,可以幫助開發(fā)人員依照系統(tǒng)需求開啟UDP套接字點(diǎn),快速建立UDP聯(lián)機(jī)來(lái)提供與遠(yuǎn)程設(shè)備通訊的功能。
這個(gè)系統(tǒng)架構(gòu)下當(dāng)增加一個(gè)不同種類的遠(yuǎn)程設(shè)備時(shí),必須要提供一個(gè)不同的UDP套接字點(diǎn),才能用來(lái)提供與不同種類遠(yuǎn)程設(shè)備通訊的功能,在遠(yuǎn)程設(shè)備種類越來(lái)越多時(shí),系統(tǒng)所需要的UDP套接字點(diǎn)就會(huì)依照遠(yuǎn)程設(shè)備種類而增加。
在遠(yuǎn)程設(shè)備種類越來(lái)越多的情景中,為了網(wǎng)絡(luò)管理考慮會(huì)限制系統(tǒng)與遠(yuǎn)程設(shè)備通訊時(shí),必須統(tǒng)一使用同一個(gè)UDP套接字點(diǎn)來(lái)與遠(yuǎn)程設(shè)備通訊,再由封包內(nèi)容、或是IP地址去判斷實(shí)際連接的遠(yuǎn)程設(shè)備為何。
class Program
{
static void Main(string[] args)
{
// Receiver
UdpClient udpClientA = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
UdpClient udpClientB = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
}
}
依照系統(tǒng)需求開發(fā)人員可能寫出上列的程序代碼,直接建立兩個(gè)UdpClient對(duì)象來(lái)開啟同一個(gè)UDP套接字點(diǎn)。這段程序代碼內(nèi)容可以通過(guò)編譯程序的檢查,但在按下執(zhí)行之后,就會(huì)在Visual Studio之中看到SocketException的例外通知,用來(lái)告知開發(fā)人員同一個(gè)套接字點(diǎn)只能被開啟一次,使用兩個(gè)UdpClient來(lái)開啟同一個(gè)套接字點(diǎn)是無(wú)法執(zhí)行的。
有涉略過(guò)Design pattern的開發(fā)人員,在遇到資源對(duì)象只能有一個(gè)實(shí)體的情景,會(huì)想到套用Singleton Pattern來(lái)提供資源對(duì)象共享的功能。系統(tǒng)中UdpClient對(duì)象所開啟的UDP套接字點(diǎn),就是屬于這種只能由一個(gè)對(duì)象所開啟的資源,這個(gè)情景中在UdpClient對(duì)象上套用Singleton Pattern看起來(lái)會(huì)是個(gè)不錯(cuò)的選擇。
class Program
{
// Singleton
private static UdpClient _udpClientInstance = null;
private static UdpClient UdpClientInstance
{
get
{
if (_udpClientInstance == null)
{
_udpClientInstance = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
}
return _udpClientInstance;
}
}
// Main
static void Main(string[] args)
{
// Receiver
UdpClient udpClientA = Program.UdpClientInstance;
UdpClient udpClientB = Program.UdpClientInstance;
// Transmiter
UdpClient transmiter = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999));
// Send
transmiter.Send(new byte[] { 55 }, 1, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
// Receive
byte[] packet = null;
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
packet = udpClientA.Receive(ref remoteEndPoint);
Console.WriteLine(string.Format("UdpClientA Receive:{0}", packet[0]));
packet = udpClientB.Receive(ref remoteEndPoint);
Console.WriteLine(string.Format("UdpClientB Receive:{0}", packet[0]));
// End
Console.ReadLine();
// Close
transmiter.Close();
udpClientB.Close();
udpClientA.Close();
}
}
將Singleton Pattern套用在系統(tǒng)內(nèi)所使用的UdpClient物件上,可以寫出上列的程序代碼,系統(tǒng)內(nèi)所使用的UdpClient對(duì)象都是取用到系統(tǒng)內(nèi)一個(gè)靜態(tài)存放的共享UdpClient對(duì)象。這段程序代碼內(nèi)容可以通過(guò)編譯程序的檢查,并且在執(zhí)行時(shí)也不會(huì)出現(xiàn)SocketException的例外通知,因?yàn)樘子肧ingleton Pattern讓系統(tǒng)內(nèi)只會(huì)開啟UDP套接字點(diǎn)一次。
但進(jìn)階一點(diǎn)去思考UdpClient對(duì)象的封包接收功能,UdpClient對(duì)象中提供Receive方法來(lái)等待、接收遠(yuǎn)程設(shè)備傳送的數(shù)據(jù)封包,收到數(shù)據(jù)封包之后再次執(zhí)行Receive方法會(huì)繼續(xù)等待、接收下一個(gè)數(shù)據(jù)封包。也就是說(shuō)一個(gè)遠(yuǎn)程設(shè)備傳送的數(shù)據(jù)封包,UdpClient只能透過(guò)Receive方法取得一次,在系統(tǒng)內(nèi)共享同UdpClient對(duì)象,沒有辦法共享Receive方法所取得的數(shù)據(jù)封包。
觀察上列范例的執(zhí)行結(jié)果,可以發(fā)現(xiàn)在范例中由transmiter所傳送的資料封包,在被UdpClientA透過(guò)Receive方法接收之后,UdpClientB無(wú)法接收到這個(gè)遠(yuǎn)程傳送的數(shù)據(jù)封包,這也就驗(yàn)證范例中將Singleton Pattern套用在系統(tǒng)內(nèi)所使用UdpClient上的方式,會(huì)發(fā)生了無(wú)法共享數(shù)據(jù)封包的問題。
為了提供系統(tǒng)使用同一個(gè)UDP套接字點(diǎn)來(lái)與遠(yuǎn)程設(shè)備通訊,再由封包內(nèi)容、或是IP地址去判斷實(shí)際連接的遠(yuǎn)程設(shè)備為何的功能。筆者設(shè)計(jì)一個(gè)名為MutualUdpClient的解決方案,用來(lái)在系統(tǒng)內(nèi)共享UDP通訊聯(lián)機(jī)并且共享遠(yuǎn)程設(shè)備傳送的數(shù)據(jù)封包。
在MutualUdpClient這個(gè)解決方案中,套用先前部落格中所發(fā)表的Singleton Pool模式,套用這個(gè)模式讓系統(tǒng)能夠共享UdpClient聯(lián)機(jī),并且在有系統(tǒng)對(duì)象使用UdpClient聯(lián)機(jī)時(shí)就開啟共享UDP通訊聯(lián)機(jī),而在所有系統(tǒng)對(duì)象都不需要使用UdpClient聯(lián)機(jī)才真正去關(guān)閉這個(gè)共享的UDP通訊聯(lián)機(jī)。
。
套用Singleton Pool模式解決了共享UdpClient聯(lián)機(jī)的功能,接著在MutualUdpClient這個(gè)解決方案中,為了共享遠(yuǎn)程設(shè)備傳送的數(shù)據(jù)封包,在UdpClient與MutualUdpClient之間加入了一個(gè)RouteUdpClient對(duì)象。
RouteUdpClient對(duì)象是一個(gè)主動(dòng)式的對(duì)象,在被建立之后會(huì)開啟一條獨(dú)立的線程,不斷的接收UdpClient所接收到的數(shù)據(jù)封包,并且將接收到數(shù)據(jù)封包透過(guò)事件的方式通知每個(gè)MutualUdpClient,經(jīng)由這樣的流程就可以將遠(yuǎn)程設(shè)備所傳送的數(shù)據(jù)封包,在每個(gè)MutualUdpClient之間共享。
而MutualUdpClient對(duì)象在收到RouteUdpClient所提供的數(shù)據(jù)封包時(shí),會(huì)先將數(shù)據(jù)封包暫存在一個(gè)隊(duì)列里,并且在MutualUdpClient對(duì)象的Receive方法被呼叫時(shí),再?gòu)年?duì)列取出數(shù)據(jù)封包并且回傳給呼叫端,用以將遠(yuǎn)程設(shè)備傳送的數(shù)據(jù)封包提供給呼叫端做后續(xù)的處理。經(jīng)由這樣的方式,每個(gè)系統(tǒng)中所建立的MutualUdpClient對(duì)象就可以透過(guò)Receive方法取得,每個(gè)遠(yuǎn)程設(shè)備傳送的數(shù)據(jù)封包。
*這邊要特別一提的是,MutualUdpClient對(duì)象不選擇事件方式來(lái)提供數(shù)據(jù)封包而采用Receive方法來(lái)提供,是為了讓使用MutualUdpClient對(duì)象的開發(fā)人員,在使用對(duì)象的時(shí)候,能夠得到與使用UdpClient一樣的開發(fā)體驗(yàn),用以減少開發(fā)時(shí)的學(xué)習(xí)時(shí)間。
處理完共享UdpClient聯(lián)機(jī)、共享遠(yuǎn)程設(shè)備傳送的資料封包之后,還要處理一下傳送數(shù)據(jù)封包到遠(yuǎn)程設(shè)備的功能。在MutualUdpClient之中,對(duì)于傳送數(shù)據(jù)封包到遠(yuǎn)程設(shè)備并沒有特殊需求,所以直接使用UdpClient的Send功能就可以完成將數(shù)據(jù)封包傳送到遠(yuǎn)程設(shè)備的功能。
class Program
{
static void Main(string[] args)
{
// Receiver
MutualUdpClient udpClientA = new MutualUdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
MutualUdpClient udpClientB = new MutualUdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
// Transmiter
UdpClient transmiter = new UdpClient(new IPEndPoint(IPAddress.Parse("127.0.0.1"), 9999));
// Send
transmiter.Send(new byte[] { 55 }, 1, new IPEndPoint(IPAddress.Parse("127.0.0.1"), 1234));
// Receive
byte[] packet = null;
IPEndPoint remoteEndPoint = new IPEndPoint(IPAddress.Any, IPEndPoint.MinPort);
packet = udpClientA.Receive(ref remoteEndPoint);
Console.WriteLine(string.Format("UdpClientA Receive:{0}", packet[0]));
packet = udpClientB.Receive(ref remoteEndPoint);
Console.WriteLine(string.Format("UdpClientB Receive:{0}", packet[0]));
// End
Console.ReadLine();
// Close
transmiter.Close();
udpClientB.Close();
udpClientA.Close();
}
}
上列程序代碼示范如何在系統(tǒng)中使用MutualUdpClient對(duì)象,在范例中可以看到程序代碼中直接建立了兩個(gè)相同UDP端點(diǎn)的MutualUdpClient對(duì)象,并且可以正常的執(zhí)行不會(huì)出現(xiàn)SocketException的例外通知。而遠(yuǎn)程設(shè)備transmiter所傳送的數(shù)據(jù)封包,在被UdpClientA透過(guò)Receive方法接收之后,UdpClientB依然可以透過(guò)Receive方法接收同一個(gè)資料,這也就驗(yàn)證了MutualUdpClient對(duì)象提供了共享通訊聯(lián)機(jī)、共享數(shù)據(jù)封包的功能。
原始碼下載: MutualUdpClientSample_jb51net.rar
相關(guān)文章
asp.net中如何批量導(dǎo)出access某表內(nèi)容到word文檔
最近有項(xiàng)目需求是這樣的,需要將某表中的每一條記錄中的某些內(nèi)容導(dǎo)出在一個(gè)word文檔中。下面小編就把我的解決辦法分享給大家,供大家參考2015-10-10ASP.net在頁(yè)面所有內(nèi)容生成后、輸出內(nèi)容前對(duì)頁(yè)面內(nèi)容進(jìn)行操作
ASP.net在頁(yè)面所有內(nèi)容生成后、輸出內(nèi)容前對(duì)頁(yè)面內(nèi)容進(jìn)行操作...2007-04-04asp.net 學(xué)習(xí)之路 項(xiàng)目整體框架簡(jiǎn)單的搭建
最近剛學(xué)了些關(guān)于asp.net mvc方面的知識(shí),于是了要拿個(gè)小項(xiàng)目來(lái)練練手,提高下自己的code能力跟思維能力2012-12-12asp.net Cookie跨域、虛擬目錄等設(shè)置方法
Cookie跨域、虛擬目錄等設(shè)置方法,需要的朋友可以參考下。2009-11-11ASP.NET實(shí)現(xiàn)圖片以二進(jìn)制的形式存入數(shù)據(jù)庫(kù)
這篇文章主要介紹了ASP.NET實(shí)現(xiàn)圖片以二進(jìn)制的形式存入數(shù)據(jù)庫(kù),有一定的學(xué)習(xí)借鑒價(jià)值,需要的朋友可以參考下2014-08-08Linux上使用Docker部署ASP.NET?Core應(yīng)用程序
這篇文章介紹了使用Docker部署ASP.NET?Core應(yīng)用程序的方法,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-03-03ASP.NET實(shí)現(xiàn)單點(diǎn)登陸(SSO)適用于多種情況
這篇文章主要介紹了ASP.NET在不同情況下實(shí)現(xiàn)單點(diǎn)登陸(SSO)的方法,在同主域但不同子域之間實(shí)現(xiàn)單點(diǎn)登陸等等2014-09-09