Visual C#建立簡單消息傳遞系統(1)
2024-07-21 02:20:26
供稿:網友
摘要:本文討論基于套接字(socket)的體系結構以及怎樣建立一個高效的、易于使用的、可以同時在pc和pocket pc上運行的消息傳遞(message-passing)系統。
套接字和消息
目前,大多數web服務和所有的遠程應用程序都使用了遠程過程調用(remote-procedure-call,rpc)方法。你所做工作好像是在調用一個函數,但是在其后臺執行了大量的操作以確保它在服務器上發生。在較低的層次,系統是在兩臺計算機之間傳遞消息,但這是不可視的。
然而,當你轉換到套接字操作的時候,你就在純粹的基于消息的系統中編程了。這會改變你編寫的代碼的類型,因為讀取返回的數據的唯一途徑是通過消息。它與使用無返回值或輸出參數的.net類有點類似,在這些情況下所有的返回信息都需要通過事件傳遞。
由于我希望服務器程序告訴客戶端什么時候應該改變曲目,使用消息就很有利,因為信息可以從服務器到達客戶端,不需要客戶端明確地請求該信息。但是,它要求你使用不同的方式達到目標。
在解釋所有操作之前,我想先談論一點點安全性方面的問題。如果你在自己的計算機上打開了某個端口,其它人可能利用這個端口做不利的事情。他們可能希望寫入沒有意義的信號,以確定自己是否能夠控制你的計算機或者使它崩潰。當你編寫這類應用程序的時候考慮一下這種可能性是必要的。我的例子將運行在防火墻后面的網絡上,所以我感覺到相對安全。
簡單的套接字
我從建立一個服務器程序開始,它能給一個整數加上1,下面是服務器端代碼:
public static void main()
{
ipaddress localaddr = ipaddress.parse("127.0.0.1");
tcplistener listener = new tcplistener(localaddr, 9999);
console.writeline("waiting for initial connection");
listener.start();
socket socket = listener.acceptsocket();
console.writeline("connected");
networkstream stream = new networkstream(socket);
binaryreader reader = new binaryreader(stream);
binarywriter writer = new binarywriter(stream);
int i = reader.readint32();
i++;
writer.write(i);
}
它首先在本機的9999端口上建立了一個tcp監聽器,接著啟動監聽器并等待連接。一旦得到了連接,它就接收一個整數,給它加1,并把它發送回去。
需要指出的是我在此處使用的本地地址是127.0.0.1。在測試的時候這種情形可以很好地運行,這個時候客戶端和服務器程序在相同的計算機上運行,但是當它們在不同的計算機上運行時,程序就不能運行了。在后面的部分中我將給出更復雜的代碼。
傳遞消息
通過套接字傳遞未經處理的數據毫無樂趣,而通過套接字傳遞對象可能更有趣一些。為了達到這個目標,我們需要一個得到對象并把它轉換為字節流的途徑。最明顯的解決方案是使用運行時(runtime)提供的串行化(serialization)支持。不幸的是,使用這種方法會有少量的問題。
第一個問題是串行化需要很大的開銷,這意味著它使用的字節比傳遞數據必要的字節多一些。如果使用soap格式化,這個問題就更嚴重。這是否成為一個問題依賴于應用程序的性能需求。第二個問題是串行化在簡潔框架組件中不能使用。由于沒有簡單的實現方法,我們需要自己做這個工作。在這個過程中,我們做的事情比串行化要少多了。
我們從建立一個枚舉開始,它定義了可以傳遞什么類型的消息:
public enum messagetype
{
requestemployee = 1,
employee,
}
對于每種消息類型,我們需要一個對象定義該對象:
public class requestemployee: isocketobject
{
int id;
public requestemployee(int id)
{
this.id = id;
}
}
public requestemployee(binaryreader reader)
{
id = reader.readint32();
}
public int id
{
get
{
return id;
}
}
public void send(binarywriter writer)
{
writer.write((int) messagetype.requestemployee);
writer.write(id);
}
}
我們使用的這種途徑與iserializable接口很相似。isocketobject接口定義了一個send()函數,它串行化通過通道的數據,接著還有一個并行化該數據的構造函數。
在這些對象中的某個串行化自身的時候,它發送的第一個信息是消息標識符。它讓接收者知道將到達哪種類型的對象并建立該對象。下面是客戶端的代碼:
requestemployee requestemployee = new requestemployee(15);
requestemployee.send(writer);
messagetype messagetype = (messagetype) reader.readint32();
switch (messagetype)
{
case messagetype.employee:
employee employee = new employee(reader);
console.writeline("{0} = {1}", employee.name, employee.address);
break;
}
requestemployee這段代碼建立了一個對象并把它發送給服務器程序,接著它找出返回的是哪種對象,并且并行化它。
盡管這個示例項目被標記為client和server,但是兩者之間唯一真正的差別是連接建立的方式。當這個過程完成后,它們都使用相似的代碼發送和接收消息,即使它們需要處理自己的消息集合。
本文來源于網頁設計愛好者web開發社區http://www.html.org.cn收集整理,歡迎訪問。