AgileEAS.NET SOA 中介軟體平臺.Net Socket通訊框架-完整應用例子-線上聊天室系統-程式碼解析
一、AgileEAS.NET SOA中介軟體Socket/Tcp框架介紹
AgileEAS.NET SOA中介軟體Socket/Tcp框架是一套Socket通訊的訊息中介軟體:
二、多人線上聊天室系統
相對於簡單的客戶端==》服務端訊息請求與應答的例子而言,線上多人聊天室系統的複雜度都要超過客戶端==》服務端訊息請求例子N多倍,但是限於文章篇幅的原因,我們沒有在文章AgileEAS.NET SOA 中介軟體平臺.Net Socket通訊框架-完整應用例子-線上聊天室系統-下載配置這中為大家介紹這個案例的具體程式碼。
下面我將為大家介紹這個案例的關鍵程式碼及閱讀、理解、修改完善所需要注意的地方。
三、關於程式碼編譯環境及其他的地些設定
本案例的原始碼在下載壓縮包的Code目錄之中,所有的引用AgileEAS.NET SOA 中介軟體平臺的程式集及客戶端、服務端執行所必須的檔案都在下載壓縮包的Publish目錄之中,所有專案的編譯輸出路徑也都是在Publish目錄,也就是所有專案不管是在Debug編譯環境還是在Release編譯環境都是輸出在Publish目錄之中,有關具體的設定請看下圖:
四、解決方案之中的專案說明
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 |
密碼 |
|
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: