InChatter系统之客户端消息处理中心
一、模块结构
首先来看下客户端消息处理中心模块的简单结构:

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下载最新版本)
InChatter系统之客户端消息处理中心的更多相关文章
- InChatter系统之客户端实现原理与阶段小结
InChatter客户端的开发可以说是目前系统的阶段性结尾了.很抱歉的是,这篇文章来的这么晚,迟到了这么久. 在客户端的开发主要针对两个方面: 消息的传输与处理 消息的UI交互处理 一.消息的传输与处 ...
- InChatter系统之服务器开发(二)
现在我们继续进行InChatter系统的服务器端的开发,今天我们将实现服务契约同时完成宿主程序的开发,今天结束之后服务器端将可以正常运行起来. 系统的开发是随着博客一起的,颇有点现场直播的感觉,所有在 ...
- iOS系统及客户端软件测试的基础介绍
iOS系统及客户端软件测试的基础介绍 iOS现在的最新版本iOS5是10月12号推出,当前版本是4.3.5 先是硬件部分,采用iOS系统的是iPad,iPhone,iTouch这三种设备,其中iPho ...
- 团队项目-北航MOOC系统Android客户端 NABC
北航MOOC系统Android客户端 NABC (N) Need 需求 MOOC的全名是Massive Open Online Course,被称作大型开放式网络课程.2012年,美国的顶尖大学陆续设 ...
- InChatter系统之服务客户端的开发
今天终于开始客户端的开发了,客户端完成以后,我们将可以进行简单的交流.开发完成的程序只是一个很简单的雏形,在本系统完成以后,以及完成的过程中,大家都可以下载源码,在此基础上融入自己的想法和尝试,可以按 ...
- InChatter系统之服务器开发(一)
服务器端是整个消息系统的中枢,类似与人类的大脑.没有他,根本无法实现客户端之间的交流,为什么呢?这也涉及到我们的系统涉及,在服务器端,每个客户端的标识数据都会在服务器端进行保存,在这种情况下,当某一个 ...
- cas单点登录系统:客户端(client)详细配置
最近一直在研究cas登录中心这一块的应用,分享一下记录的一些笔记和心得.后面会把cas-server端的配置和重构,另外还有这几天再搞nginx+cas的https反向代理配置,以及cas的证书相关的 ...
- 分布式高性能消息处理中心HPMessageCenter
# HPMessageCenter 高性能消息分发中心.用户只需写好restful接口,在portal里面配置消息的处理地址,消息消费者就会自动访问相关接口,完成消息任务. ### 部署说明 **创建 ...
- cas单点登录系统:客户端(client)详细配置(包含统一单点注销配置)
最近一直在研究cas登录中心这一块的应用,分享一下记录的一些笔记和心得.后面会把cas-server端的配置和重构,另外还有这几天再搞nginx+cas的https反向代理配置,以及cas的证书相关的 ...
随机推荐
- Class.forName() 详解
主要功能 Class.forName(xxx.xx.xx)返回的是一个类 Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类, 也就是说JVM会执行该类的静态代码段 ...
- java语法基础(一)
这个是自己前两年做java视频教程时候的课件.感兴趣的同学可以参考下. 这里是纯粹的语法行总结. editplus的使用 选择项目目录 打开editplus 左侧目录导航栏 可以打开盘符,文件夹 可以 ...
- XMU 1071 圣斗士黄金十二宫(七)银河星爆 【计算几何】
1071: 圣斗士黄金十二宫(七)银河星爆 Time Limit: 500 MS Memory Limit: 64 MBSubmit: 193 Solved: 10[Submit][Status] ...
- java根据文件流判断文件类型(后缀名)
import java.io.FileInputStream; public class FileType{ public static String bytesToHexString(byte[] ...
- YTU 2402: Common Subsequence
2402: Common Subsequence 时间限制: 1 Sec 内存限制: 32 MB 提交: 63 解决: 33 题目描述 A subsequence of a given seque ...
- YTU 2574: 空白格式化
2574: 空白格式化 时间限制: 1 Sec 内存限制: 128 MB 提交: 233 解决: 118 题目描述 恭喜你进入了蓝桥杯总决赛,本次大赛采用了全自动机器测评系统. 如果你的答案与标准 ...
- 浅谈Linux Kernel 中循环链表的实现
前阵子在弄缓存的时候,我们需要将qemu对于磁盘镜像文件写请求串成一个链表,最终将这个链表里面的写请求全部刷回到镜像文件里面,那么我们便需要一个强健,可靠的链表的接口,于是我们仿照Linux 2.4. ...
- POJ - 1459 Power Network(最大流)(模板)
1.看了好久,囧. n个节点,np个源点,nc个汇点,m条边(对应代码中即节点u 到节点v 的最大流量为z) 求所有汇点的最大流. 2.多个源点,多个汇点的最大流. 建立一个超级源点.一个超级汇点,然 ...
- C语言算法
选择排序法:用第一个数分别和后面的数比较 冒泡排序法:相邻的两个数比较 01.单词首字母大写&统计单词个数 02: 编写一个函数int pieAdd(int n),计算1!+2!+3!+……+ ...
- I.MX6 bq27441 GPOUT interrupt
/******************************************************************** * I.MX6 bq27441 GPOUT interrup ...