1. 程式人生 > >AgileEAS.NET SOA 中介軟體平臺.Net Socket通訊框架-完整應用例子-線上聊天室系統-程式碼解析

AgileEAS.NET SOA 中介軟體平臺.Net Socket通訊框架-完整應用例子-線上聊天室系統-程式碼解析

一、AgileEAS.NET SOA中介軟體Socket/Tcp框架介紹

AgileEAS.NET SOA中介軟體Socket/Tcp框架是一套Socket通訊的訊息中介軟體:

image_thumb2_thumb3_thumb_thumb

二、多人線上聊天室系統

      相對於簡單的客戶端==》服務端訊息請求與應答的例子而言,線上多人聊天室系統的複雜度都要超過客戶端==》服務端訊息請求例子N多倍,但是限於文章篇幅的原因,我們沒有在文章AgileEAS.NET SOA 中介軟體平臺.Net Socket通訊框架-完整應用例子-線上聊天室系統-下載配置這中為大家介紹這個案例的具體程式碼。

     下面我將為大家介紹這個案例的關鍵程式碼及閱讀、理解、修改完善所需要注意的地方。

三、關於程式碼編譯環境及其他的地些設定

     本案例的原始碼在下載壓縮包的Code目錄之中,所有的引用AgileEAS.NET SOA 中介軟體平臺的程式集及客戶端、服務端執行所必須的檔案都在下載壓縮包的Publish目錄之中,所有專案的編譯輸出路徑也都是在Publish目錄,也就是所有專案不管是在Debug編譯環境還是在Release編譯環境都是輸出在Publish目錄之中,有關具體的設定請看下圖:

image_thumb3

四、解決方案之中的專案說明

     ChatRoom解決方案之是共有ChatRoom.Entities、ChatRoom.BLL.Contracts、ChatRoom.BLL.Host、ChatRoom.Messages、ChatRoom.Socket、ChatingRoom.MainClient、ChatingRoom.UserManage共七個專案,其中:

    ChatRoom.Entities:是聊天室註冊用啟的資料儲存實體,其中只包括一個實體User,即註冊使用者資訊。

    ChatRoom.BLL.Contracts:為使用者管理、登入驗證、密碼找回修改等功能的分散式服務定義契約,其中僅包括一個服務契約定義IUserService(使用者服務)。

    ChatRoom.BLL.Host:為ChatRoom.BLL.Contracts所定義的服務契約的功能實現。

    ChatRoom.Messages:服務端與客戶端通訊訊息的定義,包括聊天訊息、使用者登入請求、登入結果、線上使用者清單訊息、使用者上下線狀態通知訊息。

    ChatRoom.Socket:為服務端的業務程式碼、包括AgileEAS.NET SOA服務程序的SocketService外掛以及服務端收到客戶端各種訊息的訊息處理器程式碼。

    ChatingRoom.MainClient:為客戶端程式碼、包括客戶段介面以及客戶端收到通訊訊息的訊息處理器程式碼。

五、關於SOA服務SocketService外掛

    關於這個問題就涉及到了AgileEAS.NET SOA 中介軟體平臺的SOA服務例項及Socket框架的設計,在SOA服務例項本身被設計成為了一個可以執行WCF、WS、Socket等各吃點通訊及其他應用服務的執行容器,那麼我們的Socket服務端也可以在此服務例項之中執行,同時在我們的AgileEAS.NET SOA中介軟體平臺的微核心程式集EAS.MicroKernel.dll之中定義了SocketService外掛的實現標準:

   1: using System;
   2: using System.Collections.Generic;
   3: using System.Linq;
   4: using System.Text;
   5: using EAS.Distributed;
   6:  
   7: namespace EAS.Sockets
   8: {
   9:     /// <summary>
  10:     /// SocketService服務介面定義。
  11:     /// </summary>
  12:     /// <remarks>
  13:     /// 一個Socket伺服器可以承載多種/個Socket服務,一個Socket服務處理一種業務。
  14:     /// 如IM SocketService 處理IM相關的即時通訊業務,而WF SocketService 處理工作流相關的服務,這兩種Socket服務可以同時執行在一個Socket伺服器之中。
  15:     /// </remarks>
  16:     public interface ISocketService:IAppService
  17:     {
  18:         /// <summary>
  19:         /// 使用ServerEngine初始化SocketService。
  20:         /// </summary>
  21:         /// <param name="socketServer">Socket伺服器物件。</param>
  22:         void Initialize(ISocketServerBase socketServer);
  23:     }
  24: }

    ISocketService介面中定義了一個初始化方法:void Initialize(ISocketServerBase socketServer),用於SOA服務例項完成對ISocketService例項的初始化,其中傳入引數為一個ISocketServerBase物件,其本質的含義為SOA服務例項呼叫ISocketService例項物件把SOA服務例項之中的SocketServer物件做為引數傳入,那麼我們就可以在ISocketService物件之中針對SocketServer做一些初始化工作,其中最重要的工作是,掛載與之相關的訊息物件器IMessageHandler。

    ChatRoom.Socket專案之中包括了一個ISocketService的實現ChatRoom.Socket.MessageService

   1: using EAS.Loggers;
   2: using EAS.Sockets;
   3: using System;
   4: using System.Collections.Generic;
   5: using System.Linq;
   6: using System.Text;
   7:  
   8: namespace ChatRoom.Socket
   9: {
  10:     /// <summary>
  11:     /// 聊天室訊息服務,由EAS.SOA.Server.Exe引擎的Socket初始化程式。
  12:     /// </summary>
  13:     public class MessageService : ISocketService
  14:     {
  15:         #region ISocketService 成員
  16:  
  17:         public void Initialize(EAS.Sockets.ISocketServerBase socketServer)
  18:         {
  19:             try
  20:             {
  21:                 socketServer.AddHander(new ChatMessageHandler());
  22:                 socketServer.AddHander(new LoginMessageHandler());
  23:                 ChatRoomContext.Instance.SocketServer = socketServer;
  24:             }
  25:             catch (System.Exception exc)
  26:             {
  27:                 Logger.Error(exc);
  28:             }
  29:  
  30:             socketServer.SessionStarted += socketServer_SessionStarted;
  31:             socketServer.SessionAbandoned += socketServer_SessionAbandoned;
  32:         }
  33:  
  34:         void socketServer_SessionStarted(object sender, NetSessionEventArgs e)
  35:         {
  36:             Logger.Info(string.Format("Session:{0}  Started", e.Session.SessionID));
  37:         }
  38:  
  39:         void socketServer_SessionAbandoned(object sender, NetSessionEventArgs e)
  40:         {
  41:             Logger.Info(string.Format("Session:{0}  Abandoned", e.Session.SessionID));
  42:         }
  43:  
  44:         //void socketServer_MessagerReceived(object sender, EAS.Sockets.MessageEventArgs e)
  45:         //{
  46:         //    Logger.Info(string.Format("MessagerReceived:{0}", e.Message.ToString()));
  47:         //}
  48:  
  49:  
  50:         //void socketServer_MessageSend(object sender, EAS.Sockets.MessageEventArgs e)
  51:         //{
  52:         //    Logger.Info(string.Format("MessageSend:{0}", e.Message.ToString()));
  53:         //}
  54:  
  55:         public void Start()
  56:         {
  57:  
  58:         }
  59:  
  60:         public void Stop()
  61:         {
  62:  
  63:         }
  64:  
  65:         #endregion
  66:     }
  67: }

    其中最重要的程式碼是Initialize函式之中掛載ChatMessage、LoginMessage兩個訊息的訊息處理器程式碼:

   1: socketServer.AddHander(new ChatMessageHandler());
   2: socketServer.AddHander(new LoginMessageHandler());

    Socket外掛服務的定義除了程式碼定義之外,還需要在AgileEAS.NET SOA 中介軟體有SOA服務例項配置檔案之中進行定義,因為SOA服務例項程式有32位和64位版本,分別為EAS.SOA.Server.exe和EAS.SOA.Server.x64.exe,所以要根據自身的機器條件和自己喜歡的執行環境修改EAS.SOA.Server.exe.config或EAS.SOA.Server.x64.exe.config:

   1: <?xml version="1.0"?>
   2: <configuration>
   3:   <configSections>
   4:     <section name="eas" type="EAS.ConfigHandler,EAS.MicroKernel"/>
   5:   </configSections>
   6:   <!--支援混合程式集-->
   7:   <startup useLegacyV2RuntimeActivationPolicy="true">
   8:     <supportedRuntime version="v4.0"/>
   9:   </startup>
  10:   <eas>
  11:     <configurations>
  12:       <item name="Key"  value="Value"/>
  13:     </configurations>
  14:     <appserver>
  15:       <channel>
  16:         <wcf enable="true">
  17:           <config tcpPort="6907" httpPort="6908"/>
  18:           <serviceThrottling maxConcurrentCalls="128" maxConcurrentInstances="128" maxConcurrentSessions="256"/>
  19:           <wcfServices>
  20:             <wcfService key="Key" type="Value"/>
  21:           </wcfServices>
  22:         </wcf>
  23:         <socket enable ="true">
  24:           <config tcpPort="6906"/>
  25:           <serviceThrottling maxConcurrence="8196"/>
  26:           <socketServices>
  27:             <socketService key="MessageService" type="ChatRoom.Socket.MessageService,ChatRoom.Socket"/>
  28:           </socketServices>
  29:         </socket>
  30:       </channel>
  31:       <appServices>
  32:         <service key="Key" type="Value"/>
  33:       </appServices>
  34:     </appserver>
  35:     <objects>
  36:       <!--資料訪問提供者物件-->
  37:       <object name="DbProvider" assembly="EAS.Data.Provider" type="EAS.Data.Access.SqliteProvider" LifestyleType="Thread">
  38:         <property name="ConnectionString" type="string" value="Data Source=..\db\Chat.db;" />
  39:       </object>
  40:       <!--資料訪問器-->
  41:       <object name="DataAccessor" assembly="EAS.Data" type="EAS.Data.Access.DataAccessor" LifestyleType="Thread">
  42:         <property name="DbProvider" type="object" value="DbProvider"/>
  43:         <property name="Language" type="object" value="SqliteLanguage"/>
  44:       </object>
  45:       <!--ORM訪問器-->
  46:       <object name="OrmAccessor" assembly="EAS.Data" type="EAS.Data.ORM.OrmAccessor" LifestyleType="Thread">
  47:         <property name="DataAccessor" type="object" value="DataAccessor"/>
  48:       </object>
  49:       <!--本地服務橋-->
  50:       <object name="ServiceBridger" assembly="EAS.MicroKernel" type="EAS.Services.DirectServiceBridger" LifestyleType="Singleton" />
  51:       <!--Linq查詢語言-->
  52:       <object name="SqliteLanguage" assembly="EAS.Data.Provider" type="EAS.Data.Linq.SqliteLanguage" LifestyleType="Thread"/>
  53:       <!--日誌記錄-->
  54:       <object name="Logger" assembly="EAS.MicroKernel" type="EAS.Loggers.TextLogger" LifestyleType="Singleton">
  55:         <property name="Path" type="string" value="..\logs" />
  56:       </object>
  57:       <!--分散式服務上下文引數定義。-->
  58:       <object name="EAS.Distributed.ServiceContext" type="EAS.Distributed.ServiceContext,EAS.SOA.Server" LifestyleType="Singleton">
  59:         <property name="EnableLogging" type="bool" value="false" />
  60:       </object>
  61:     </objects>
  62:   </eas>
  63:   <startup>
  64:     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0"/>
  65:   </startup>
  66: </configuration>

     需要在  <eas/appserver/channel/socket/socketServices>配置節中之中增加了一端:

   1: <socketService key="MessageService" type="ChatRoom.Socket.MessageService,ChatRoom.Socket"/>

     用於告訴SOA服務例項在啟動的時候載入並初始化型別為“ChatRoom.Socket.MessageService,ChatRoom.Socket”的SocketService。

六、註冊使用者資料庫及Sqlite介紹

    線上多人聊到室系統之中有登入、使用者,那麼也就必須有資料庫,要儲存這些註冊使用者的資訊,為了方便這案例的使用和部署,我們選擇了輕量級的Sqlite檔案資料庫,其特別是簡單方便,對於小資料量儲存非常好用,有關於Sqlite的知識請自己從網上學習,本人使用的sqlite管理工具為SQLite Expert。

    註冊使用者表結構如下:

Ø CHAT_USER(聊天室使用者表)

表名

CHAT_USER

所有者

dbo

列名

資料型別

說明

LOGINID

NVARCHAR(64)

N

登入ID

Name

NVARCHAR(64)

Y

使用者名稱

PASSWORD

NVARCHAR(64)

Y

密碼

MAIL

VARCHAR(128)

Y

郵件

SafeKey

NVARCHAR(64)

Y

密碼找回問題

SafeResult

NVARCHAR(64)

Y

密碼找回答案

STATE

BIT

Y

狀態

REGTIME

DATETIME

Y

註冊時間

    有關針對CHAT_USER表的資料訪問使用了AgileEAS.NET SOA中介軟體平臺的ORM及與之配套的Linq進行訪問,其對應的ORM實體物件為ChatRoom.Entities.User:

   1: using System;
   2: using System.Linq;
   3: using System.ComponentModel;
   4: using System.Data;
   5: using EAS.Data;
   6: using EAS.Data.Access;
   7: using EAS.Data.ORM;
   8: using EAS.Data.Linq;
   9: using System.Runtime.Serialization;
  10:  
  11: namespace ChatRoom.Entities
  12: {
  13:    /// <summary>
  14:    /// 實體物件 User(聊天室使用者表)。
  15:    /// </summary>
  16:    [Serializable()]
  17:    [Table("CHAT_USER","聊天室使用者表")]
  18:    partial class User: DataEntity<User>, IDataEntity<User>
  19:    {
  20:        public User()
  21:        {
  22:            this.RegTime = DateTime.Now;
  23:        }
  24:        
  25:        protected User(SerializationInfo info, StreamingContext context)
  26:            : base(info, context)
  27:        {
  28:        }
  29:        
  30:        #region O/R對映成員
  31:  
  32:        /// <summary>
  33:        /// 登入ID 。
  34:        /// </summary>
  35:        [Column("LOGINID","登入ID"),DataSize(64),PrimaryKey]
  36:        [DisplayName("登入ID")]
  37:        public string LoginID
  38:        {
  39:            get;
  40:            set;
  41:        }
  42:  
  43:        /// <summary>
  44:        /// 使用者名稱 。
  45:        /// </summary>
  46:        [Column("Name","使用者名稱"),DataSize(64)]
  47:        [DisplayName("使用者名稱")]
  48:        public string Name
  49:        {
  50:            get;
  51:            set;
  52:        }
  53:  
  54:        /// <summary>
  55:        /// 密碼 。
  56:        /// </summary>
  57:        [Column("PASSWORD","密碼"),DataSize(64)]
  58:        [DisplayName("密碼")]
  59:        public string Password
  60:        {
  61:            get;
  62:            set;
  63:        }
  64:  
  65:        /// <summary>
  66:        /// 郵件 。
  67:        /// </summary>
  68:        [Column("MAIL","郵件"),DataSize(128)]
  69:        [DisplayName("郵件")]
  70:        public string Mail
  71:        {
  72:            get;
  73:            set;
  74:        }
  75:  
  76:        /// <summary>
  77:        /// 密碼找回問題 。
  78:        /// </summary>
  79:        [Column("SafeKey","密碼找回問題"),DataSize(64)]
  80:        [DisplayName("密碼找回問題")]
  81:        public string SafeKey
  82:        {
  83:            get;
  84:            set;
  85:        }
  86:  
  87:        /// <summary>
  88:        /// 密碼找回答案 。
  89:        /// </summary>
  90:        [Column("SafeResult","密碼找回答案"),DataSize(64)]
  91:        [DisplayName("密碼找回答案")]
  92:        public string SafeResult
  93:        {
  94:            get;
  95:            set;
  96:        }
  97:  
  98:        /// <summary>
  99:        /// 狀態 。
 100:        /// </summary>
 101:        [Column("STATE","狀態")]
 102:        [DisplayName("狀態")]
 103:        public int State
 104:        {
 105:            get;
 106:            set;
 107:        }
 108:  
 109:        /// <summary>
 110:        /// 註冊時間 。
 111:        /// </summary>
 112:        [Column("REGTIME","註冊時間")]
 113:        [DisplayName("註冊時間")]
 114:        public DateTime RegTime
 115:        {
 116:            get;
 117: