一、模块结构

首先来看下客户端消息处理中心模块的简单结构:

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系统之客户端消息处理中心的更多相关文章

  1. InChatter系统之客户端实现原理与阶段小结

    InChatter客户端的开发可以说是目前系统的阶段性结尾了.很抱歉的是,这篇文章来的这么晚,迟到了这么久. 在客户端的开发主要针对两个方面: 消息的传输与处理 消息的UI交互处理 一.消息的传输与处 ...

  2. InChatter系统之服务器开发(二)

    现在我们继续进行InChatter系统的服务器端的开发,今天我们将实现服务契约同时完成宿主程序的开发,今天结束之后服务器端将可以正常运行起来. 系统的开发是随着博客一起的,颇有点现场直播的感觉,所有在 ...

  3. iOS系统及客户端软件测试的基础介绍

    iOS系统及客户端软件测试的基础介绍 iOS现在的最新版本iOS5是10月12号推出,当前版本是4.3.5 先是硬件部分,采用iOS系统的是iPad,iPhone,iTouch这三种设备,其中iPho ...

  4. 团队项目-北航MOOC系统Android客户端 NABC

    北航MOOC系统Android客户端 NABC (N) Need 需求 MOOC的全名是Massive Open Online Course,被称作大型开放式网络课程.2012年,美国的顶尖大学陆续设 ...

  5. InChatter系统之服务客户端的开发

    今天终于开始客户端的开发了,客户端完成以后,我们将可以进行简单的交流.开发完成的程序只是一个很简单的雏形,在本系统完成以后,以及完成的过程中,大家都可以下载源码,在此基础上融入自己的想法和尝试,可以按 ...

  6. InChatter系统之服务器开发(一)

    服务器端是整个消息系统的中枢,类似与人类的大脑.没有他,根本无法实现客户端之间的交流,为什么呢?这也涉及到我们的系统涉及,在服务器端,每个客户端的标识数据都会在服务器端进行保存,在这种情况下,当某一个 ...

  7. cas单点登录系统:客户端(client)详细配置

    最近一直在研究cas登录中心这一块的应用,分享一下记录的一些笔记和心得.后面会把cas-server端的配置和重构,另外还有这几天再搞nginx+cas的https反向代理配置,以及cas的证书相关的 ...

  8. 分布式高性能消息处理中心HPMessageCenter

    # HPMessageCenter 高性能消息分发中心.用户只需写好restful接口,在portal里面配置消息的处理地址,消息消费者就会自动访问相关接口,完成消息任务. ### 部署说明 **创建 ...

  9. cas单点登录系统:客户端(client)详细配置(包含统一单点注销配置)

    最近一直在研究cas登录中心这一块的应用,分享一下记录的一些笔记和心得.后面会把cas-server端的配置和重构,另外还有这几天再搞nginx+cas的https反向代理配置,以及cas的证书相关的 ...

随机推荐

  1. [IT学习]学习Python过程需要记忆的一些坑

    1.列表的引用和复制 A byte of Python 中文4.05c版本85页 单纯对列表进行引用,则列表指向同一对象. 如果你需要复制一份全新的拷贝,则需要通过切片操作. 2.仅有一个元素的元组, ...

  2. casperjs userAgent的一些问题

    casperjs 的options内的userAgent若设置为非正常浏览器的字符串,可能导致form无法正确提交. 表现为,this.click()失效,或evaluate(function(){$ ...

  3. [Django基础] django解决静态文件依赖问题以及前端引入方式

    一.静态文件依赖 学习django的时候发现静态文件(css,js等)不能只在html中引入,还要在项目的settings中设置,否则会报以下错误 [11/Sep/2018 03:18:15] &qu ...

  4. bzoj1566 [NOI2009]管道取珠——DP

    题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1566 一眼看上去很懵... 但是答案可以转化成有两个人在同时取珠子,他们取出来一样的方案数: ...

  5. 520. Detect Capital(检测数组的大小写)

    Given a word, you need to judge whether the usage of capitals in it is right or not. We define the u ...

  6. ubuntu 怎么格式化U盘?(转载)

    转自:http://3168247.blog.51cto.com/3158247/605654 图形的话装一个gparted,找那个/dev/sdb,右击选择格式化,最后点“应用”.命令行:原则是先卸 ...

  7. Linux 常用命令七 grep

    一.grep命令 grep(global search regular expression(RE) and print out the line,全面搜索正则表达式并把行打印出来)是一种强大的文本搜 ...

  8. bzoj 3205: [Apio2013]机器人【dfs+斯坦纳树+spfa】

    第一次听说斯坦纳树这种东西 先dfs预处理出来dis[i][j][k]表示格子(i,j)向k方向转移能到哪,记忆话搜索预处理,注意如果有环的话特判一下 设f[i][j][x][y]表示复合机器人i-j ...

  9. 【Aizu - 0005 】GCD and LCM

    GCD and LCM Descriptions: Write a program which computes the greatest common divisor (GCD) and the l ...

  10. WCF、WebAPI、WebService之间的区别

    Web Service 1.它是基于SOAP协议的,数据格式是XML 2.只支持HTTP协议 3.它不是开源的,但可以被任意一个了解XML的人使用 4.它只能部署在IIS上 WCF 1.这个也是基于S ...