在文章AgileEAS.NET SOA 中間件平臺.Net Socket通訊框架-介紹一文之中咱們對AgileEAS.NET SOA中間Socket/Tcp框架進行了整體的介紹,咱們知道git
AgileEAS.NET SOA中間件Socket/Tcp框架是一套Socket通訊的消息中間件:程序員
在文章AgileEAS.NET SOA 中間件平臺.Net Socket通訊框架-簡單例子-實現簡單的服務端客戶端消息應答給你們實例介紹了有關於AgileEAS.NET SOA 中間件Socket通訊框架的簡單應用以後,咱們經過文章AgileEAS.NET SOA 中間件平臺.Net Socket通訊框架-完整應用例子-在線聊天室系統-下載配置向你們展現了一個完整成熟的.NET Socket 通訊框架的應用案例,一個從在線聊天室系統,經過文章向你們講解了如何下載和編譯安案例源代碼、以及如何配置服務端和客戶段。github
相對於簡單的客戶端==》服務端消息請求與應答的例子而言,在線多人聊天室系統的複雜度都要超過客戶端==》服務端消息請求例子N多倍,可是限於文章篇幅的緣由,咱們沒有在文章AgileEAS.NET SOA 中間件平臺.Net Socket通訊框架-完整應用例子-在線聊天室系統-下載配置這中爲你們介紹這個案例的具體代碼。sql
下面我將爲你們介紹這個案例的關鍵代碼及閱讀、理解、修改完善所須要注意的地方。數據庫
本案例的源代碼在下載壓縮包的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,即註冊用戶信息。session
ChatRoom.BLL.Contracts:爲用戶管理、登陸驗證、密碼找回修改等功能的分佈式服務定義契約,其中僅包括一個服務契約定義IUserService(用戶服務)。app
ChatRoom.BLL.Host:爲ChatRoom.BLL.Contracts所定義的服務契約的功能實現。框架
ChatRoom.Messages:服務端與客戶端通訊消息的定義,包括聊天消息、用戶登陸請求、登陸結果、在線用戶清單消息、用戶上下線狀態通知消息。
ChatRoom.Socket:爲服務端的業務代碼、包括AgileEAS.NET SOA服務進程的SocketService插件以及服務端收到客戶端各類消息的消息處理器代碼。
ChatingRoom.MainClient:爲客戶端代碼、包括客戶段界面以及客戶端收到通訊消息的消息處理器代碼。
若是對比AgileEAS.NET SOA 中間件平臺.Net Socket通訊框架-簡單例子-實現簡單的服務端客戶端消息應答,細心的朋友必定會發現本案例中沒有了相似Socket.Demo.Server功能的項目,而是多了ChatRoom.Socket項目。
關於這個問題就涉及到了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 Expert。
註冊用戶表結構以下:
表名 |
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: set;
118: }
119:
120: #endregion
121: }
122: }
針對CHAT_USER表的用戶登陸、註冊驗證、找回密碼等代碼的實如今ChatRoom.BLL.Host.UserService之中:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using EAS.Services;
6: using ChatRoom.Entities;
7: using EAS.Data.ORM;
8: using EAS.Data.Linq;
9:
10: namespace ChatRoom.BLL
11: {
12: /// <summary>
13: /// 帳號服務。
14: /// </summary>
15: [ServiceBind(typeof(IUserService))]
16: public class UserService : IUserService
17: {
18: public void AddUser(User user)
19: {
20: using (DbEntities db = new DbEntities())
21: {
22: user.RegTime = DateTime.Now;
23: int count = db.Users.Where(p => p.LoginID == user.LoginID).Count();
24: if (count>0)
25: {
26: throw new System.Exception(string.Format("已經存在帳號爲{0}的用戶", user.LoginID));
27: }
28:
29: db.Users.Insert(user);
30: }
31: }
32:
33: public User UserLogin(string loginID, string password)
34: {
35: using (DbEntities db = new DbEntities())
36: {
37: var v = db.Users.Where(p => p.LoginID == loginID).FirstOrDefault();
38: if (v == null)
39: {
40: throw new System.Exception(string.Format("不存在登帳號稱爲{0}的用戶", loginID));
41: }
42:
43: if (v.Password != password)
44: {
45: throw new System.Exception("密碼不正肯定");
46: }
47:
48: return v;
49: }
50: }
51:
52: public bool UserExists(string loginID)
53: {
54: using (DbEntities db = new DbEntities())
55: {
56: int count = db.Users.Where(p => p.LoginID == loginID).Count();
57: return count > 0;
58: }
59: }
60:
61: public string GetSafeKey(string loginID)
62: {
63: using (DbEntities db = new DbEntities())
64: {
65: var v = db.Users.Where(p => p.LoginID == loginID).FirstOrDefault();
66: if (v != null)
67: {
68: return v.SafeKey;
69: }
70: else
71: {
72: return string.Empty;
73: }
74: }
75: }
76:
77: public string GetSafeResult(string loginID)
78: {
79: using (DbEntities db = new DbEntities())
80: {
81: var v = db.Users.Where(p => p.LoginID == loginID).FirstOrDefault();
82: if (v != null)
83: {
84: return v.SafeResult;
85: }
86: else
87: {
88: return string.Empty;
89: }
90: }
91: }
92:
93:
94: public void ChangePassword(string loginID, string password)
95: {
96: using (DbEntities db = new DbEntities())
97: {
98: db.Users.Update(p => new User { Password = password }, p => p.LoginID == loginID);
99: }
100: }
101: }
102: }
系統中如何知道目有那些用戶在線,參考以上六節的內容咱們能夠知道,用戶的主鍵是帳號ID,與SocketServer之中在線清單NetSession沒有特定的關係,那麼如何創建這種關係,多而獲得目前有那些用戶在線呢,在ChatRoom.Socket項目之中咱們定義了LoginInfo對象:
1: using EAS.Sockets;
2: using System;
3: using System.Collections.Generic;
4: using System.Linq;
5: using System.Text;
6: using ChatRoom.Entities;
7:
8: namespace ChatRoom.Socket
9: {
10: /// <summary>
11: /// 消息信息類。
12: /// </summary>
13: public class LoginInfo
14: {
15: /// <summary>
16: /// 登陸帳號。
17: /// </summary>
18: public string LoginID
19: {
20: get;
21: set;
22: }
23:
24: /// <summary>
25: /// 用戶對象。
26: /// </summary>
27: public User User
28: {
29: get;
30: set;
31: }
32:
33: /// <summary>
34: /// 會話。
35: /// </summary>
36: public NetSession Session { get; set; }
37: }
38:
39: }
咱們看源代碼就能夠明確的知道,他是一個用於創建NetSession與登陸用戶LoginID/User對象的影射類,即某個登陸用戶是在那個網絡會話這上,咱們在ChatRoom.Socket項目之中定義了一個ChatRoomContext上下文輔助類:
1: using EAS.Sockets;
2: using ChatRoom.Messages;
3: using System;
4: using System.Collections.Generic;
5: using System.Linq;
6: using System.Text;
7: using System.Runtime.CompilerServices;
8:
9: namespace ChatRoom.Socket
10: {
11: class ChatRoomContext
12: {
13: #region 單例模式
14:
15: private static object m_Lock = new object();
16: private static ChatRoomContext m_Instance = null;
17:
18: public static ChatRoomContext Instance
19: {
20: get
21: {
22: if (m_Instance == null)
23: {
24: lock (m_Lock)
25: {
26: if (m_Instance == null)
27: {
28: m_Instance = new ChatRoomContext();
29: }
30: }
31: }
32:
33: return m_Instance;
34:
35: }
36: }
37:
38: ChatRoomContext()
39: {
40: this.m_LoginInfos = new List<LoginInfo>();
41: this.m_OnLineMessage = new OnLineMessage();
42: }
43:
44: #endregion
45:
46: ISocketServerBase m_SocketServer = null;
47: List<LoginInfo> m_LoginInfos = null;
48: OnLineMessage m_OnLineMessage = null;
49:
50: /// <summary>
51: /// Socket服務器。
52: /// </summary>
53: public ISocketServerBase SocketServer
54: {
55: get
56: {
57: return this.m_SocketServer;
58: }
59: set
60: {
61: this.m_SocketServer = value;
62: }
63: }
64:
65: /// <summary>
66: /// 會話集合。
67: /// </summary>
68: public List<LoginInfo> LoginInfos
69: {
70: get
71: {
72: return this.m_LoginInfos;
73: }
74: }
75:
76: /// <summary>
77: /// 在線清單信息。
78: /// </summary>
79: public OnLineMessage OnLineMessage
80: {
81: get
82: {
83: return this.m_OnLineMessage;
84: }
85: }
86:
87: /// <summary>
88: /// 根據Socket會話求上下文信息。
89: /// </summary>
90: /// <param name="sessionID"></param>
91: /// <returns></returns>
92: public LoginInfo GetLoginInfo(Guid sessionID)
93: {
94: LoginInfo v = null;
95: lock (this.m_LoginInfos)
96: {
97: v = this.m_LoginInfos.Where(p => p.Session.SessionID == sessionID).FirstOrDefault();
98: }
99:
100: return v;
101: }
102:
103: /// <summary>
104: /// 根據帳號求上下文信息。
105: /// </summary>
106: /// <param name="LoginID"></param>
107: /// <returns></returns>
108: public LoginInfo GetLoginInfo(string LoginID)
109: {
110: LoginInfo v = null;
111: lock (this.m_LoginInfos)
112: {
113: v = this.m_LoginInfos.Where(p => p.LoginID == LoginID).FirstOrDefault();
114: }
115:
116: return v;
117: }
118:
119: /// <summary>
120: /// 登陸註冊上下文。
121: /// </summary>
122: /// <param name="info"></param>
123: public void Add(LoginInfo info)
124: {
125: lock (this.m_LoginInfos)
126: {
127: int count = this.m_LoginInfos.Where
128: (p => p.Session.SessionID == info.Session.SessionID
129: && p.LoginID == info.LoginID
130: ).Count();
131:
132: if (count == 0)
133: {
134: this.m_LoginInfos.Add(info);
135: info.Session.ClientClosed += Session_ClientClosed;
136: }
137: }
138:
139: this.CreateOnLineMesssage();
140: }
141:
142: /// <summary>
143: /// 連接關機上下文。
144: /// </summary>
145: /// <param name="session"></param>
146: public void Remove(Guid session)
147: {
148: lock (this.m_LoginInfos)
149: {
150: LoginInfo info = this.m_LoginInfos.Where(p => p.Session.SessionID == session).FirstOrDefault();
151:
152: if (info != null)
153: {
154: this.m_LoginInfos.Remove(info);
155: info.Session.ClientClosed -= new EventHandler(Session_ClientClosed);
156: }
157: }
158: }
159:
160: /// <summary>
161: /// 生成在線清單信息。
162: /// </summary>
163: [MethodImpl(MethodImplOptions.Synchronized)]
164: void CreateOnLineMesssage()
165: {
166: this.m_OnLineMessage = new OnLineMessage();
167: lock (this.m_LoginInfos)
168: {
169: foreach (var item in this.m_LoginInfos)
170: {
171: OnLine onLine = new OnLine();
172: onLine.LoginID = item.LoginID;
173: onLine.Name = item.User.Name;
174: this.m_OnLineMessage.OnLines.Add(onLine);
175: }
176: }
177: }
178:
179: /// <summary>
180: /// 客戶段鏈接斷開,用戶下線。
181: /// </summary>
182: /// <param name="sender"></param>
183: /// <param name="e"></param>
184: private void Session_ClientClosed(object sender, EventArgs e)
185: {
186: NetSession session = sender as NetSession;
187: LoginInfo loginInfo = this.GetLoginInfo(session.SessionID);
188: this.Remove(session.SessionID);
189: this.CreateOnLineMesssage();
190:
191: //向其餘用戶發生下線通稿。
192: UserStateMessage userState = new UserStateMessage();
193: userState.OnLine = false;
194: userState.User = loginInfo.User;
195:
196: lock (this.m_LoginInfos)
197: {
198: foreach (var item in this.m_LoginInfos)
199: {
200: ChatRoomContext.Instance.SocketServer.Send(item.Session.SessionID, userState);
201: }
202: }
203: }
204: }
205: }
其中public List<LoginInfo> LoginInfos屬性即爲當前在線的用戶與網絡會話(NetSession)的映射清單。
客戶端在處理用戶登陸時執行如下流程:
執行本流程的具體代碼在ChatRoom.Socket項目之中的登陸消息處理器LoginMessageHandler之中:
1: using EAS.Sockets;
2: using EAS.Sockets.Messages;
3: using System;
4: using System.Collections.Generic;
5: using System.Linq;
6: using System.Text;
7: using System.Data;
8: using EAS.Data.Access;
9: using ChatRoom.Messages;
10: using EAS.Loggers;
11: using ChatRoom.BLL;
12: using ChatRoom.Entities;
13: using EAS.Services;
14:
15: namespace ChatRoom.Socket
16: {
17: /// <summary>
18: /// 用戶登陸消息處理程序。
19: /// </summary>
20: public class LoginMessageHandler : AbstractMessageHandler<LoginMessage>
21: {
22: public override void Process(NetSession context, uint instanceId, LoginMessage message)
23: {
24: LoginResultMessage result = new LoginResultMessage();
25: IUserService services = ServiceContainer.GetService<IUserService>();
26: try
27: {
28: result.User = services.UserLogin(message.LoginID, message.PassWord);
29: }
30: catch (System.Exception exc)
31: {
32: result.Error = exc.Message;
33: }
34:
35: //X.登陸失敗。
36: if (!string.IsNullOrEmpty(result.Error))
37: {
38: context.Reply(result);
39: return;
40: }
41:
42: //A.登陸成功,作以下處理
43:
44: #region //1.向其發送登陸成功消息
45:
46: context.Reply(result);
47:
48: #endregion
49:
50: #region //2.向其餘用戶發送上線通告
51:
52: UserStateMessage userState = new UserStateMessage();
53: userState.OnLine = true;
54: userState.User = result.User;
55:
56: var vList = ChatRoomContext.Instance.LoginInfos;
57: if (vList.Count > 0)
58: {
59: lock (vList)
60: {
61: foreach (var item in vList)
62: {
63: ChatRoomContext.Instance.SocketServer.Send(item.Session.SessionID, userState);
64: }
65: }
66: }
67:
68: #endregion
69:
70: #region //3.註冊到上下文環境
71:
72: LoginInfo loginInfo = new LoginInfo();
73: loginInfo.LoginID = message.LoginID;
74: loginInfo.User = result.User;
75: loginInfo.Session = context;
76: ChatRoomContext.Instance.Add(loginInfo);
77: #endregion
78:
79: #region //4.向客戶段發送在線清單
80:
81: context.Reply(ChatRoomContext.Instance.OnLineMessage);
82:
83: #endregion
84: }
85: }
86: }
當客戶端下線/斷開連接以後服務端會向其餘在線的客戶段發送一個UserStateMessage狀態通告消息,告訴其餘在線客戶端,某人已經下線。
當服務端接收到客戶端發來的聊天消息以後,如何轉發呢,請參見下圖:
關於這一部分的代碼請參考ChatRoom.Socket項目之中的聊天消息處理器ChatMessageHandler之中::
1: using EAS.Sockets;
2: using EAS.Sockets.Messages;
3: using ChatRoom.Messages;
4: using System;
5: using System.Collections.Generic;
6: using System.Linq;
7: using System.Text;
8:
9: namespace ChatRoom.Socket
10: {
11: /// <summary>
12: /// 服務器收到聊天消息處理程序。
13: /// </summary>
14: public class ChatMessageHandler : AbstractMessageHandler<ChatMessage>
15: {
16: public override void Process(NetSession context, uint instanceId, ChatMessage message)
17: {
18: if (!message.Secret) //廣播消息。
19: {
20: lock (ChatRoomContext.Instance.LoginInfos)
21: {
22: foreach (var p in ChatRoomContext.Instance.LoginInfos)
23: {
24: context.Server.Send(p.Session.SessionID, message);
25: }
26: }
27: }
28: else
29: {
30: LoginInfo loginInfo = ChatRoomContext.Instance.GetLoginInfo(message.To);
31: if (loginInfo != null)
32: {
33: context.Server.Send(loginInfo.Session.SessionID, message);
34: }
35: }
36: }
37: }
38: }
關於這一部分的代碼請參考:
由於AgileEAS.NET SOA 中間件平臺Socket 通訊框架何用的是異步消息處理模式,因此當客戶端收到服務器發回的消息的時候其工做線程與界面UI線呢不一致,那麼UI界面處理的時候咱們就須要異步處理,好比在顯示收到的ChatMessage的時候:
1: /// <summary>
2: /// 顯示聊天消息。
3: /// </summary>
4: /// <param name="chat"></param>
5: internal void ShowMessage(ChatMessage chat)
6: {
7: Action action = () =>
8: {
9: string form = "你";
10: string to = "你";
11:
12: //其餘人。
13: if (chat.From != AppContext.User.LoginID)
14: {
15: var v = this.m_OnLines.Where(p => p.LoginID == chat.From).FirstOrDefault();
16: if (v != null)
17: form = v.Name;
18: else
19: form = chat.From;
20: }
21:
22: //全部人
23: if (string.IsNullOrEmpty(chat.To))
24: {
25: to = DEFAULT_ALL_USER;
26: }
27: else //
28: {
29: var v = this.m_OnLines.Where(p => p.LoginID == chat.To).FirstOrDefault();
30: if (v != null)
31: to = v.Name;
32: else
33: to = chat.From;
34: }
35:
36: string face = string.IsNullOrEmpty(chat.Face) ? string.Empty : string.Format("{0}地", chat.Face);
37: string Text = string.Format("【{0}】{1}{2}對【{3}】說:{4}", form, chat.Action, face, to, chat.Content);
38:
39: ListViewItem item = new ListViewItem(new string[] { string.Empty, Text });
40: item.ForeColor = Color.FromArgb(chat.Color);
41: item.Tag = chat.From;
42:
43: if (chat.Secret) //密聊
44: {
45: this.lvSecret.Items.Add(item);
46: this.lvSecret.EnsureVisible(item.Index);
47: }
48: else
49: {
50: this.lvAll.Items.Add(item);
51: this.lvAll.EnsureVisible(item.Index);
52: }
53: };
54:
55: this.Invoke(action);
56: }
咱們定義了一個名稱爲action的匿名方法,使用this.Invoke(action)進行界面的消息顯示。
爲了完善、改進和推廣AgileEAS.NET而成立了敏捷軟件工程實驗室,是一家研究、推廣和發展新技術,並致力於提供具備自主知識產權的業務基礎平臺軟件,以及基於業務基礎平臺了開發的管理軟件的專業軟件提供商。主要業務是爲客戶提供軟件企業研發管理解決方案、企業管理軟件開發,以及相關的技術支持,管理及技術諮詢與培訓業務。
AgileEAS.NET平臺自2004年秋呱呱落地一來,我就一直在逐步完善和改進,也被應用於保險、醫療、電子商務、房地產、鐵路、教育等多個應用,但一直都是以我我的在推廣,2010年由於我辭職休息,我就想到把AgileEAS.NET推向市場,讓更多的人使用。
個人技術團隊成員都是合做多年的老朋友,由於這個平臺是免費的,因此也沒有什麼收入,都是由程序員的那種理想與信念堅持,在此我感謝一塊兒奮鬥的朋友。
AgileEAS.NET網站:http://www.agileeas.net
官方博客:http://eastjade.cnblogs.com
github:https://github.com/agilelab/eas
QQ:47920381
AgileEAS.NET QQ羣:
113723486(AgileEAS SOA 平臺)/上限1000人
199463175(AgileEAS SOA 交流)/上限1000人
120661978(AgileEAS.NET 平臺交流)/上限1000人
212867943(AgileEAS.NET研究)/上限500人
147168308(AgileEAS.NET應用)/上限500人
172060626(深度AgileEAS.NET平臺)/上限500人
116773358(AgileEAS.NET 平臺)/上限500人
125643764(AgileEAS.NET探討)/上限500人
193486983(AgileEAS.NET 平臺)/上限500人
郵件:james@agilelab.cn,mail.james@qq.com,
電話:18629261335。