ESFramework的初體驗


  第一次接觸ESFramework,心中難免會有一絲喜悅與激動,想第一時間和大家分享這一個星期以來我學習ESFramework的體會...當然還有很多疑問有待在后面的學習中去解決。

  暫不說技術,在這里談談我自己初步學習ESFramework的一些體會,當然也會對一個基於ESFramework的即時通信的Demo進行簡單的闡述,希望對於大家學習ESFramework會有少許幫助!

  前幾天從官網上下載了ESFramework的學習文檔與Demo。下載地址:http://www.oraycn.com/ESFramework_detail.aspx,在這里我們可以下載到ESFramework的開發手冊和一些使用技巧,還有Demo。  

  

  在這里給初學者一個小的建議,不要先去看Demo,因為這樣你會不知所雲。我開始也是看了開發手冊的第一篇概述,然后就迫不及待的去研究代碼,最后發現越看越糊塗,根本就不知道講的什么,因為里面都是一些陌生的類、接口等,也不知道這些類具體能為我們做什么,如:RapidPassiveEngine、ICustomizeOutters、IManagedForm,哈哈,模糊了吧,因為你根本就不認識。我的建議是,先通讀開發手冊的前十篇,大體了解ESFramework是什么,核心組件是哪些,基本體系是怎樣的,一些方法內部是如何實現了。如果你大體了解了這些你就可以開始去學習一些Demo了。當然還有一個前提是你必須要對委托、事件、接口都能理解,因為Demo中大量使用了這些。

  接觸任何新事物,都會有一個循序漸進的過程,從了解到認知,從陌生到熟悉。對於一門全新的技術也是如此,你必須先得了解它是什么(what),然后再問為什么(why),最后才是怎么用(do)。當然對於初學者的我們,我個人覺得我們可以先去了解它是什么,然后學着別人怎么用,最后在不斷學習的過程中去理解它是為什么,內部是如何實現的。來吧,開始我們的學習!

  

  一:ESFramework是什么?

  首先我們必須明白,ESFramework 是一套性能卓越、穩定可靠、可高度伸縮、靈活易用而又功能強大的.NET通信框架。既然是一個通訊框架,那么它的應用當然也會非常的廣泛了,即時通訊系統(IM),大型多人在線游戲(MMORPG)、在線網頁游戲(WebGame)、文件傳送系統(FTS)、數據采集系統(DGS)、分布式OA系統、遠程教育系統等等,凡是需要分布式通信的系統都可以使用ESFramework框架。當然ESFramework的問世,當然會有它獨特之處,也就說它相對其它的通信框架有它的優點:高性能,可靠、穩定,可靠的P2P,高伸縮性的群集平台,跨平台,可擴展性強。(開發手冊有詳細的介紹)。

      ESFramework體系直接構建在.NET Framework 2.0上,它由通信核心ESFramework、應用增強ESPlus、以及群集平台ESPlatform構成。 

  為了快速而高效地構建應用程序,我們可以基於ESPlus進行開發。ESPlus內置眾多組件供我們直接使用,像消息頭、解析器、消息處理器、序列化器、自定義信息、文件傳送、P2P通道、好友/組友狀態改變通知、等等。

  欲掌握ESPlus快速開發,需要抓住三個方面:Rapid引擎、四大武器、兩翼。

  1.Rapid引擎

  在了解ESPlus的開發之前我們必須得了解Rapid引擎,引擎又分為服務端引擎(RapdiServerEngine)和客戶端引擎RapidPassiveEngine。這里的引擎我們大可理解為我們日常生活中的發動機引擎,不管是摩托車、小汽車還是大貨車,都需要這個發動機引擎。暫時不必關心它的內部實現,直接拿來用就好了,在使用之前必須對其初始化,初始化成功后,我們就可以使用引擎對象暴露出的四大武器了。正如發動機引擎啟動后,我們就可以控制其加速、減速、剎車等;

     2.四大武器都都位於ESPlus.Application空間下:

  • ESPlus.Application.CustomizeInfo 命名空間:用於發送和處理自定義信息。
  • ESPlus.Application.Basic             命名空間:用於完成基礎功能(如獲取在線好友列表等)、和接收用戶狀態改變通知(如好友上下線等)。
  • ESPlus.Application.FileTransfering 命名空間:用於完成與文件傳送相關的所有功能。
  • ESPlus.Application.P2PSession     命名空間:用於完成與P2P打洞、P2P通信相關的所有功能。 

  3.  兩翼

   “兩翼”指的就是IFriendManager與IGroupManager,后面服務器模塊會有用到,主要是對好友和組的一個管理。

 

  二:怎么用?(Demo的學習)

   從官網上下載的Demo名為:“1.ESFramework.Demos.Simplest”,這個Demo主要是實現 客戶端用戶上下線時,通知其他在線用戶;當客戶端與服務端網絡斷開時,進行自動重連;當網絡恢復后,重連成功;所有在線用戶之間可以進行文字聊天;重登陸模式當同名的用戶登陸時,會把前面的用戶擠掉等。   

      1.先看看整個Demo的框架

   整個解決方法下有三個項目,一個客戶端、一個服務端,還有個一個公共的類庫Demos.Core,在客戶端和服務端都可以引用它。

      2.先說說公共的類庫,公共的類庫中有2個自定義類,一個是InformationTypes類   

/// <summary>
/// 自定義信息的類型
/// </summary>
public static class InformationTypes
{
/// <summary>
/// 聊天信息
/// </summary>
public const int Chat = 0;

/// <summary>
/// 客戶端同步調用服務端
/// </summary>
public const int ClientCallServer = 100;
}

       自定義信息類中定義了2個常量一個表示聊天消息,一個表示客戶端同步調用服務器。再看TextChatContract類

 /// <summary>
/// TextChatContract 文字交談協議。
/// </summary>
[Serializable]
public class TextChatContract : BaseSerializeContract
{
#region Ctor
public TextChatContract()
{
}
public TextChatContract(string _text)
{
this.text = _text;
}
public TextChatContract(string _text, bool _containsForeignObject, SortedArray<int, uint> _ary,Font _font, Color _foreColor)
{
this.text = _text;
this.containsForeignObject = _containsForeignObject;
this.localEmotionArray = _ary;
this.font = _font;
this.foreColor = _foreColor;
}
#endregion
...
//其它字段和屬性的定義
}

   這就是定義的關於聊天消息的類,其中包括了文字消息,還有可以有截圖,表情,字體及其它顏色。

      3.再看看Client項目下有幾個窗體,一個登錄窗體LoginForm,一個客戶端管理主窗體MainForm,一個聊天窗體ChatForm,一個加載表情的窗體EmotionForm,還有一個用戶控件TextChatControl。顧名思義,我們可以想象幾個窗體分別的用途。

          1)Program.cs

    static class Program
{
[STAThread]
static void Main()
{
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);


RapidPassiveEngine rapidPassiveEngine = new RapidPassiveEngine();
MainForm mainForm = new MainForm();
LoginForm loginForm = new LoginForm(rapidPassiveEngine, mainForm); //在LoginForm中初始化客戶端引擎RapidPassiveEngine
if (loginForm.ShowDialog() != DialogResult.OK)
{
return;
}

mainForm.Initialize(rapidPassiveEngine);

Application.Run(mainForm);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}
}

          2) 先實例化一個服務器端的引擎RapidPassiveEngine對象,然后在LoginForm中初始化客戶端引擎

public LoginForm( RapidPassiveEngine engine ,MainForm _mainForm)
{
InitializeComponent();
this.rapidPassiveEngine = engine;
this.mainForm = _mainForm;
}

    3)登陸界面

View Code
 private void button_login_Click(object sender, EventArgs e)
{
string userID = this.textBox_id.Text.Trim();
//判斷用戶名的長度
if (userID.Length > 10)
{
MessageBox.Show("ID長度必須小於10.");
return;
}
//默認登錄為成功狀態
LogonResult logonResult = LogonResult.Succeed;
try
{
this.rapidPassiveEngine.SystemToken = "" ; //系統標志

//初始化引擎並登錄,返回登錄結果。如果登陸成功,引擎將與當前用戶綁定。
logonResult = this.rapidPassiveEngine.Initialize(userID, this.textBox_pwd.Text, "127.0.0.1" ,4530, this.mainForm).LogonResult;
}
catch (Exception ee)
{
MessageBox.Show(string.Format("連接服務器失敗。{0}" ,ee.Message));
return;
}

//登錄失敗的幾種原因
if (logonResult == LogonResult.Failed)
{
MessageBox.Show("用戶名或密碼錯誤!");
return;
}

if (logonResult == LogonResult.HadLoggedOn)
{
MessageBox.Show("已經在其它地方登陸!");
return;
}
this.DialogResult = DialogResult.OK;
}

  Demo默認任何用戶登錄都成功,LogonResponse是在ESPlus.Application.Basic命名空間下的一個枚舉類型。引擎在初始化方法中分別要傳入幾個參數(用戶名,密碼,服務器IP,服務器端口,自定義信息),如果登錄成功,服務器引擎將與當前用戶綁定。

   4)登錄成功后運行,運行主窗體,主窗體就是一個管理聊天的中心,里面包括對消息的處理,還包括斷線、重連、好友的上線與下線、重復登錄、心跳超時、自己被踢或被擠下線等處理;這就需要用的我們的四大武器之一:ESPlus.Application.CustomizeInfo 命名空間:用於發送和處理自定義信息。

   因為我們這是客戶端程序所有必須得引用using ESPlus.Application.CustomizeInfo.Passive;這有在這個命名空間下我們才能實現ICustomizeHandler這個接口,而這個接口中正是定義了(消息處理、斷線等問題)處理問題的對應方法;

  所以在主窗體初始化時,我們就預定了這些事件:

View Code
public void Initialize(RapidPassiveEngine engine)
{
this.userID = engine.CurrentUserID;
this.rapidPassiveEngine = engine;

//預訂斷線事件
this.rapidPassiveEngine.TcpPassiveEngine.ConnectionInterrupted += new CbGeneric(tcpPassiveEngine_ConnectionInterrupted);
//預訂重連失敗事件
this.rapidPassiveEngine.TcpPassiveEngine.ConnectionRebuildFailure += new CbGeneric(tcpPassiveEngine_ConnectionRebuildFailure);
//預訂重連開始事件
this.rapidPassiveEngine.TcpPassiveEngine.ConnectionRebuildStart += new CbGeneric(tcpPassiveEngine_ConnectionRebuildStart);
//預訂重連成功事件
this.rapidPassiveEngine.TcpPassiveEngine.ConnectionRebuildSucceed += new CbGeneric(tcpPassiveEngine_ConnectionRebuildSucceed);
//預訂好友上線的事件
this.rapidPassiveEngine.BasicOutter.FriendConnected += new CbGeneric<string>(BasicOutter_FriendConnected);
//預訂好友下線的事件
this.rapidPassiveEngine.BasicOutter.FriendOffline += new CbGeneric<string>(BasicOutter_FriendOffline);
//預定重復登陸時的事件
this.rapidPassiveEngine.BasicOutter.HadLogon += new CbGeneric(BasicOutter_HadLogon);
//預訂自己心跳超時的事件
this.rapidPassiveEngine.BasicOutter.TimeoutOffline += new CbGeneric(BasicOutter_TimeoutOffline);
//預訂自己被踢出掉線的事件
this.rapidPassiveEngine.BasicOutter.BeingKickedOut += new CbGeneric(BasicOutter_BeingKickedOut);
//預訂自己被擠掉線的事件
this.rapidPassiveEngine.BasicOutter.BeingPushedOut += new CbGeneric(BasicOutter_BeingPushedOut);

this.toolStripLabel_loginfo.Text = string.Format("當前登錄:{0}", this.userID);
this.toolStripLabel_state.Text = "連接狀態:正常";

this.InitializeFriends();
}

  在主窗體中還定義了處理來自其它用戶的聊天消息的方法,這個方法是實現了ICustomizeHandler接口,也是我們提到過的四大武器中的一個用途,ICustomizeHandler

在ESPlus.Application.CustomizeInfo.Passive命名空間下。

View Code
/// <summary>
/// 處理來自其它用戶的聊天消息
/// </summary>
public void HandleInformation(string sourceUserID, int informationType, byte[] info)
{
if (this.InvokeRequired)
{
this.Invoke(new CbGeneric<string,int, byte[]>(this.HandleInformation), sourceUserID,informationType, info);
}
else
{
if (informationType == InformationTypes.Chat)
{

ChatForm form = this.chatFormManager.GetForm(sourceUserID);
if (form == null)
{
form = new ChatForm(this.userID, sourceUserID, this.rapidPassiveEngine.CustomizeOutter);
this.chatFormManager.Add(form);
form.Show();
}

form.Focus();

TextChatContract contract = (TextChatContract)ESBasic.Helpers.SerializeHelper.DeserializeBytes(info, 0, info.Length);
form.ShowOtherTextChat(sourceUserID, contract);
}
}
}

  我們還可以看到在主窗體初始化方法中調用了this.InitializeFriends();//這就是加載所有的好友列表將其顯示在主窗體界面中。方法如下:

View Code
        private void InitializeFriends()
{
List<string> list = this.rapidPassiveEngine.BasicOutter.GetAllOnlineUsers();
foreach (string friendID in list)
{
if (friendID != this.userID && !this.ListViewContains(friendID))
{
this.listView1.Items.Add(friendID, 0);
}
}
}

#region ListViewContains
private bool ListViewContains(string userID)
{
for (int i = 0; i < this.listView1.Items.Count; i++)
{
if (this.listView1.Items[i].Text == userID)
{
return true;
}
}
return false;
}

  在主窗體中還定義了一個全局的private FormManager<string, ChatForm> chatFormManager = new FormManager<string, ChatForm>();我們可以簡單的將其理解為一個字典dit,主要是用來管理我們所有的聊天窗口ChatForm,這樣我們的聊天窗口就必須得實現IManagedForm<string>這一泛型接口;

  ChatForm form = this.chatFormManager.GetForm(friendID);//通過ID獲取聊天窗體


  5)當所有默認的好友都加載成功后,我們就可以雙擊主窗體中的好友頭像,這時就會彈出一個聊天窗口ChatForm;

  整個界面分為消息的顯示,表情面板,以及消息的發送框和”發送”,Demo將這些功能都定義在了用戶控件TextChatControl中了;

  點擊發送按鈕,將用戶信息發送給其它用戶或者服務端:

View Code
        private void button_send_Click(object sender, EventArgs e)
{
bool containsForeignObject = false;
SortedArray<int, uint> ary = this.agileRichTextBox_send.GetAllImage(out containsForeignObject);

if (this.agileRichTextBox_send.Text.Trim() == "" && ary.Count == 0 && (!containsForeignObject))
{
return;
}
string text = "";

this.agileRichTextBox_history.AppendRichText(string.Format("{0} {1}\n ",this.userID, DateTime.Now) ,null,null,Color.DarkGreen);
if (containsForeignObject)
{
this.agileRichTextBox_history.AppendRtf(this.agileRichTextBox_send.Rtf);// AppendRichText(this.agileRichTextBox_send.Text, ary, this.agileRichTextBox_send.Font, this.agileRichTextBox_send.ForeColor);
text = this.agileRichTextBox_send.Rtf;

}
else
{
this.agileRichTextBox_history.AppendRichText(this.agileRichTextBox_send.Text, ary, this.agileRichTextBox_send.Font, this.agileRichTextBox_send.ForeColor);
text = this.agileRichTextBox_send.Text;
}

TextChatContract contract = new TextChatContract(text, containsForeignObject, ary, this.agileRichTextBox_send.Font, this.agileRichTextBox_send.ForeColor);
this.agileRichTextBox_history.AppendText("\n");
this.agileRichTextBox_history.ScrollToCaret();
this.agileRichTextBox_send.Clear();

//發送消息給好友
this.customizeInfoOutter.Send(this.destUserID, InformationTypes.Chat, ESBasic.Helpers.SerializeHelper.SerializeObject(contract));

}

   在發送消息之前必須准備好要發送的消息內容contract;(公共類庫中定義文字交談協議)

  然后調用ICustomizeInfoOutter類型的對象的Send()方法(對方ID,消息類型,序列化后的消息);

  發送消息的方式有多種(開發手冊(一)有介紹),我們這里的Send()方法,是只向在線用戶targetUserID發送消息;還有SendByP2PChannel(),是只通P2P通道向targetUserID發送消息。

  對話框中顯示對方消息的方法:

View Code
        public void ShowOtherTextChat(string userID, TextChatContract contract)
{
if (this.InvokeRequired)
{
this.Invoke(new CbGeneric<string, TextChatContract>(this.ShowOtherTextChat), userID, contract);
}
else
{
if (userID != this.userID)
{

this.agileRichTextBox_history.AppendRichText(string.Format("{0} {1}\n ", userID, DateTime.Now), null, null, Color.DarkGreen);
if (contract.ContainsForeignObject)
{
this.agileRichTextBox_history.AppendRtf(contract.Text);// AppendRichText(this.agileRichTextBox_send.Text, ary, this.agileRichTextBox_send.Font, this.agileRichTextBox_send.ForeColor);

}
else
{
this.agileRichTextBox_history.AppendRichText(contract.Text, contract.LocalEmotionArray, contract.Font, contract.ForeColor);
}

this.agileRichTextBox_history.AppendText("\n");
this.agileRichTextBox_history.ScrollToCaret();
this.agileRichTextBox_send.Clear();
}
}
}

        

  4.服務端

  1)program.cs  (前面提到過“兩翼”的使用)

View Code
        private static ESPlus.Rapid.RapidServerEngine RapidServerEngine = new ESPlus.Rapid.RapidServerEngine();

[STAThread]
static void Main()
{
try
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);

//如果是其它類型的授權用戶,請使用下面的語句設定正確的授權用戶ID和密碼。
ESFramework.AuthorizationVerifier.SetAuthorizedUser(AuthorizationVerifier.FreeUser, "");

//使用簡單的好友管理器,假設所有在線用戶都是好友。(僅僅用於demo)
ESPlus.Core.Server.DefaultFriendManager friendManager = new ESPlus.Core.Server.DefaultFriendManager();

//初始化服務端引擎
RapidServerEngine.Initialize(4530, new CustomizeHandler(), new BasicHandler(), friendManager, null);
friendManager.UserManager = RapidServerEngine.UserManager; //RapidServerEngine初始化成功后,其UserManager屬性才可用。

//設置重登陸模式
RapidServerEngine.UserManager.RelogonMode = RelogonMode.ReplaceOld;

//如果不需要默認的UI顯示,可以替換下面這句為自己的Form
ESPlus.Widgets.MainServerForm mainForm = new ESPlus.Widgets.MainServerForm(RapidServerEngine);
Application.Run(mainForm);
}
catch (Exception ee)
{
MessageBox.Show(ee.Message);
}
}

  在Program.cs中實例化一個服務端引擎,可以設定正確的授權用戶ID(Demo中是免費用戶)和密碼;可以設定好友組(默認所有登錄到服務器的都是好友),還可以設定用戶的登錄模式,以及設定是否使用默認的服務器管理窗體:

  2)BasicBusinessHandler.cs和CustomizeHandler.cs分別定義對客戶端用戶名、密碼的驗證和對客戶端消息的處理(實現ESPlus.Application.CustomizeInfo.Server.ICustomizeHandler接口)。

  ICustomizeHandler接口定義了2個方法:

  

      //處理來自客戶端的自定義信息

void HandleInformation(string sourceUserID, int informationType, byte[] info);

  //處理來自客戶端的請求並返回應答信息]

  byte[] HandleQuery(string sourceUserID, int informationType, byte[] info);

  

  這大致就是我學習這個Demo的一個思路與少許感觸,或許你仍然不知道ESFramework是什么,不知道ESFramework在這個Demo中是如何體現的。但我們必須清楚這里面的核心是什么:兩個引擎(RapdiServerEngine和RapidPassiveEngine)、四大武器、兩翼,在這個Demo中都有體現。比如說我們發送和處理文字消息時都用到了四大武器之一:ESPlus.Application.CustomizeInfo。在這里ESFramework通信框架的優勢就凸現出來了,因為發送消息不必像socket編程那樣繁瑣,我們只需要調用Send()方法就可以了,我們也不必深究其什么通信協議之類的東西,因為ESFramework的內部引擎全部替我們做好了這些事情。在我們發送消息的時候還可以選擇不同的方式(如:發送同步消息,選擇P2P通道發送等)去發送。還有發送文件我們有用到另一大武器:ESPlus.Application.FileTransfering ;還比如在服務器端我們也可以直接去調用一些接口(好友上下線,自己被擠下線,不同的登錄方式,好友分組等)快速的去實現一些功能。

  就此擱筆,如果此時的您也和一樣編程經驗不足,也是初步了解ESFramework,那您就有必要和我一樣自己重復的將Demo寫上一遍或幾遍,通過反復的讀文檔,看Demo,相信您會有不一樣的收獲!

      


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM