程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> Visual C#建立簡單消息傳遞系統(1)

Visual C#建立簡單消息傳遞系統(1)

編輯:關於C語言
摘要:本文討論基於套接字(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,但是兩者之間唯一真正的差別是連接建立的方式。當這個過程完成後,它們都使用相似的代碼發送和接收消息,即使它們需要處理自己的消息集合。
  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved