一、模块结构
首先来看下客户端消息处理中心模块的简单结构:
ChatCallback:服务器端我们定义的回调接口IChatCallback的客户端实现
ChatMsgCenter:服务端的消息处理中心,所有的消息都将在这里进行分发处理,可以比作人的大脑中枢
ClientContext:登录信息描述,也可以理解为客户端唯一标识
DataHelper:数据库操作类,这里我们使用NDatabase的开源对象数据库,使用方法参考关法文档
Messager:消息类封装,在消息的基础上,添加了ID属性和IsRead属性
二、技术实现
1.ChatCallbck的实现原理
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InChatter.Client.Service { public class ChatCallback:ChatServer.IChatCallback { public event Action<ChatServer.InChatterMessage> OnMessgeReceived; public void ReceiveMsg(ChatServer.InChatterMessage message) { if (OnMessgeReceived != null) { OnMessgeReceived(message); } } } }
在ChatcallBack类中,我们添加了OnMessageReceived事件处理,由于回调的触发在服务器端,所以当服务器端调用ReceiveMsg方法时,我们为ChatCallback注册的事件,便可以执行并捕获对应的数据。
2.Messager消息类的封装
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InChatter.Client.Service { public class Messager { public string Id { set; get; } public bool IsRead { set; get; } public ChatServer.InChatterMessage Message { set; get; } public Messager() { Id = Guid.NewGuid().ToString(); } } }
正如我前面解释的那样,这里对InChatterMessage进行了封装,添加了Id以便于我们进行相应的数据库等操作,而IsRead标识了消息的阅读状态
3.DataHelper类的实现
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InChatter.Client.Service { public class DataHelper { #region Save public static bool Save(Messager message) { try { lock (string.Intern(ChatMsgCenter.Instance.DbUrl)) { using (var db = NDatabase.OdbFactory.Open(ChatMsgCenter.Instance.DbUrl)) { db.Store(message); } } return true; } catch { return false; } } public static bool Save(IEnumerable<Messager> msgList) { try { lock (string.Intern(ChatMsgCenter.Instance.DbUrl)) { using (var db = NDatabase.OdbFactory.Open(ChatMsgCenter.Instance.DbUrl)) { foreach (var msg in msgList) { db.Store(msg); } } } return true; } catch { return false; } } #endregion public static bool Update(Messager message) { try { lock (string.Intern(ChatMsgCenter.Instance.DbUrl)) { using (var db = NDatabase.OdbFactory.Open(ChatMsgCenter.Instance.DbUrl)) { var msg = (from item in db.AsQueryable<Messager>() where item.Id == message.Id select item).Single(); msg.IsRead = message.IsRead; msg.Message = message.Message; db.Store(msg); } } return true; } catch { return false; } } public static bool Delete(string msgId) { try { lock (string.Intern(ChatMsgCenter.Instance.DbUrl)) { using (var db = NDatabase.OdbFactory.Open(ChatMsgCenter.Instance.DbUrl)) { var msg = (from item in db.AsQueryable<Messager>() where item.Id == msgId select item).Single(); db.Delete(msg); } } return true; } catch { return false; } } } }
这里我们使用的NDatabase的开源对象数据库,这里有一个需要注意的地方是NDatabase没有明确的update方法,它使用的是读取并更新的方式,即从数据库中读取的数据,而后直接进行的操作并调用Store方法,将处理为更新(操作代码在同一个区域块内),这里需要特别注意,否则将会存储很多相同的实例,而无法应用更新。
4.ClientContext类的实现原理
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InChatter.Client.Service { public class ClientContext { static Lazy<ClientContext> _Instance = new Lazy<ClientContext>(); public static ClientContext Instance { get { return _Instance.Value; } } public string LoginId { set; get; } public string LoginName { set; get; } } }
这里目前通过LoginId和LoginName标识登录状态,而Login将被用来标识客户端Id
5.ChatMsgCenter消息处理中心类
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace InChatter.Client.Service { public class ChatMsgCenter { private static Lazy<ChatMsgCenter> _Instance = new Lazy<ChatMsgCenter>(); public static ChatMsgCenter Instance { get { return _Instance.Value; } } public string DbUrl { set; get; } public ChatServer.ChatClient Client { set; get; } private ChatCallback callback { set; get; } public event Action<ChatServer.InChatterMessage> OnMessageReceived; public event Action<Messager> OnUnReadMessageRead; private ChatMsgCenter() { callback = new ChatCallback(); Client = new ChatServer.ChatClient(new System.ServiceModel.InstanceContext(callback), "NetTcpBinding_IChat"); callback.OnMessgeReceived+=callback_OnMessgeReceived; } private void callback_OnMessgeReceived(ChatServer.InChatterMessage message) { Messager msg = new Messager() { IsRead = false, Message = message, }; DataHelper.Save(msg); if (OnMessageReceived != null) { OnMessageReceived(message); } } public void Login(string clientId) { Client.Login(clientId); DbUrl = "Messages\"+clientId+".db"; } public void SendMsg(ChatServer.InChatterMessage message) { Messager msg = new Messager() { IsRead = true, Message = message, }; DataHelper.Save(msg); Client.SendMsg(msg.Message); } public void Logout(string clientId) { Client.Logout(clientId); } } }
这里是使用单件来实现的系统类,并且还应用了延迟加载类的辅助LazyLoad,LazyLoad类的具体用法参考这里
在Login时,我们向服务器发送Login请求,并设置当前登录ClientContext的信息,同时设置数据存储地址,客户端将根据登录人ID来标识,每个人的存储都对应到自己Id地址的数据库中。
以上是整个客户端系统的重要部分,欢迎大家讨论,并提供宝贵意见
源码提供给大家:下载源码(到CodePlex下载最新版本)