COMET彗星(三)构建自己的COMET核心

主题列表:

COMET彗星(一)SERVER PUSH介绍

COMET彗星(二)基于SERVER PUSH的消息传输

引言:

      在上一篇随笔中,对COMET使用的类和作用进行了简短的介绍,从本篇随笔开始,将从实体类开始,对COMET的核心进行构建分析。

CORE框架:

      

图1.1 COMET核心框架

CometMessage类:

     CometMessage类是COMET的通信载体,对消息的主体进行抽象,实际上这个类是最容易进行扩展的,因为从设计上看,它只是一个消息的容器。而诸如地理坐标,业务数据等,都可以通过这个类来进行直接扩充。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; namespace MethodWorx.AspNetComet.Core { /// <summary> /// CometMessage Class /// /// This is a CometMessage that has been sent to the client, the DataContract names have been /// shortened to remove any bytes we dont need from the message (ok, did'nt save much, but we can do it!) /// </summary> [DataContract(Name="cm")] public class CometMessage { [DataMember(Name="mid")] private long messageId; [DataMember(Name="n")] private string name; [DataMember(Name="c")] private object contents; /// <summary> /// Gets or Sets the MessageId, used to track which message the Client last received /// </summary> public long MessageId { get { return this.messageId; } set { this.messageId = value; } } /// <summary> /// Gets or Sets the Content of the Message /// </summary> public object Contents { get { return this.contents; } set { this.contents = value; } } /// <summary> /// Gets or Sets the error message if this is a failure /// </summary> public string Name { get { return this.name; } set { this.name = value; } } } }

 

      类的设计简单明了,这里有必要解释下使用System.Runtime.Serialization命名空间的意义。

      “System.Runtime.Serialization 命名空间包含可用于将对象序列化和反序列化的类。序列化是将对象或对象图形转换为线性字节序列,以存储或传输到另一个位置的过程。反序列化是接受存储的信息并利用它重新创建对象的过程。”

      这是MSDN给我们的解释,将对象转变为线性字节,然后方便传输与调用。当然这个例子中的数据类型并不复杂,但也包含了LONG,OBJECT,STRING这样的数据类型。其中Contents成员为object对象,这给我们留下了非常大的想像空间。(图片?复杂对象类型?自定义对象类型?……)

 

CometClient类:

      CometClient类是对客户端信息的抽象类,同时包含了两个关键属性ConnectionIdleSeconds和ConnectionTimeoutSeconds。由于考虑到不同客户端间传递属性,仍然使用System.Runtime.Serialization来序列化信息。

      关于JSON的应用,这个框架其实也提供了相应支持。后面的随笔中我会介绍。

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Runtime.Serialization; namespace MethodWorx.AspNetComet.Core { /// <summary> /// CometClient Class /// /// This represents a logged in client within the COMET application. This marked as a DataContract becuase /// it can be seralized to the client using JSON /// </summary> [DataContract] public class CometClient { [DataMember] private string privateToken; [DataMember] private string publicToken; [DataMember] private string displayName; [DataMember] private DateTime lastActivity; [DataMember] private int connectionIdleSeconds; [DataMember] private int connectionTimeoutSeconds; /// <summary> /// Gets or Sets the token used to identify the client to themselves /// </summary> public string PrivateToken { get { return this.privateToken; } set { this.privateToken = value; } } /// <summary> /// Gets or Sets the token used to identify the client to other clients /// </summary> public string PublicToken { get { return this.publicToken; } set { this.publicToken = value; } } /// <summary> /// Gets or Sets the display name of the client /// </summary> public string DisplayName { get { return this.displayName; } set { this.displayName = value; } } /// <summary> /// Gets or Sets the last activity of the client /// </summary> public DateTime LastActivity { get { return this.lastActivity; } set { this.lastActivity = value; } } /// <summary> /// Gets or Sets the ConnectionIdleSections property which is the number of seconds a connection will remain /// alive for without being connected to a client, after this time has expired the client will /// be removed from the state manager /// </summary> public int ConnectionIdleSeconds { get { return this.connectionIdleSeconds; } set { this.connectionIdleSeconds = value; } } /// <summary> /// Gets or Sets the ConnectionTimeOutSections property which is the number of seconds a connection will remain /// alive for whilst being connected to a client, but without receiving any messages. After a timeout has expired /// A client should restablish a connection to the server /// </summary> public int ConnectionTimeoutSeconds { get { return this.connectionTimeoutSeconds; } set { this.connectionTimeoutSeconds = value; } } } }

ConnectionIdleSeconds:用来设置连接线程,当connection断线后,后台Thread的存活时间。

ConnectionTimeoutSeconds:客户端的超时时间,当超过时间后,客户端重新连接服务器。

ps:有这两个属性后,基本上完成了客户端连接的控制,超时重连接,无连接时杀死后台线程。

ICometStateProvider接口:

     ICometStateProvider接口直接被CometStateMessager建立,这样的好处是实例化CometStateMessager对象后,CometStateMessager对象可以直接调用ICometStateProvider接口的实现,实际上实现了Adapter的方式,我们可以定制不同的InProcCometStateProvider类(在下面会提到)来定制自己的接口。

      ICometStateProvider接口类提供了如下几个接口。

 

      InitializeClient:提供ComentClient的初始化操作。

      GetMessages:得到一个Messages的消息队列。

      SendMessage:发送Message数据。

      SendMessage:可以针对Client name来进行消息发布(私人会话)。

      GetCometClient:返回一个Client。

      KillIdleCometClient:杀掉一个无效的Client对象。

 

      ps:当然这个接口是可以被拓展的,而且实现起来非常简单。得到Client队列信息,得到Client状态等等。甚至你可以想象一些更复杂的应用(比如加入一个流媒体消息……)。

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MethodWorx.AspNetComet.Core { /// <summary> /// This interface can be implemented to provide a custom state provider /// for the CometStateManager class. Typical examples may be using SqlServer /// to enable the operation over a server farm /// </summary> public interface ICometStateProvider { /// <summary> /// Implementation of this method should store the cometClient instance in some sort /// of cache (eg Memory, Db etc..) /// </summary> /// <param name="cometClient"></param> void InitializeClient(CometClient cometClient); /// <summary> /// Imeplementation of this method should return all the messages that are queued /// for a specific client, it is only interested in messages that have a greater id than /// lastMessageId /// </summary> /// <param name="clientPrivateToken"></param> /// <param name="lastMessageId"></param> /// <returns></returns> CometMessage[] GetMessages(string clientPrivateToken, long lastMessageId); /// <summary> /// Implementation of this method should queue a message for the specific client /// </summary> /// <param name="clientPublicToken"></param> /// <param name="name"></param> /// <param name="contents"></param> void SendMessage(string clientPublicToken, string name, object contents); /// <summary> /// Implementation of this method should queue a message for all the clients /// </summary> /// <param name="name"></param> /// <param name="contents"></param> void SendMessage(string name, object contents); /// <summary> /// Implementation of this method should return a specific comet client /// </summary> /// <param name="clientPrivateToken"></param> /// <returns></returns> CometClient GetCometClient(string clientPrivateToken); /// <summary> /// Implementation of this method should remove a client from the cache /// </summary> /// <param name="clientPrivateToken"></param> void KillIdleCometClient(string clientPrivateToken); } }

InProcCometStateProvider类:

      InProcCometStateProvider类实现了ICometStateProvider接口,并且提供了一个很好的范例,针对这个类,我们可以想象很多很好的拓展,诸如调用AO组件,封装报警信息等等。

      InProcCometStateProvider类包含类一个私有的内部类InProcCometStateProvider,实际上可以理解为一种对消息的简单封装,设计的时候考虑到需要关联CometClient和Message,其实也可以单独作为一个外部类来设计。不过Adapter本身不应该太多关联类,这样做也是权衡了一些拓展上的需求。

using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace MethodWorx.AspNetComet.Core { /// <summary> /// Class InProcCometStateProvider /// /// This class provides an implementation of ICometStateProvider that keeps the /// information in memory. This provider is not scalable as it will not run on a server /// farm but demonstrates how you should implemement the provider. /// </summary> public class InProcCometStateProvider : ICometStateProvider { /// <summary> /// Private class which holds the state of each connected client /// </summary> private class InProcCometClient { public CometClient CometClient; public Dictionary<long, CometMessage> Messages = new Dictionary<long, CometMessage>(); public long NextMessageId = 1; } /// <summary> /// Cache of clients /// </summary> private Dictionary<string, InProcCometClient> publicClients = new Dictionary<string, InProcCometClient>(); private Dictionary<string, InProcCometClient> privateClients = new Dictionary<string, InProcCometClient>(); private static object state = new object(); #region ICometStateProvider Members /// <summary> /// Store the new client in memory /// </summary> /// <param name="cometClient"></param> public void InitializeClient(CometClient cometClient) { if (cometClient == null) throw new ArgumentNullException("cometClient"); lock (state) { // ok, ensure we dont already exist if (publicClients.ContainsKey(cometClient.PublicToken) || privateClients.ContainsKey(cometClient.PrivateToken)) throw CometException.CometClientAlreadyExistsException(); InProcCometClient inProcCometClient = new InProcCometClient() { CometClient = cometClient }; // stick the client int he arrays // ready to be used publicClients.Add(cometClient.PublicToken, inProcCometClient); privateClients.Add(cometClient.PrivateToken, inProcCometClient); } // ok, they are in there ready to be used } /// <summary> /// Get the messages for a specific client /// </summary> /// <param name="clientPrivateToken"></param> /// <param name="lastMessageId"></param> /// <returns></returns> public CometMessage[] GetMessages(string clientPrivateToken, long lastMessageId) { if(string.IsNullOrEmpty(clientPrivateToken)) throw new ArgumentNullException("clientPrivateToken"); lock (state) { if (!privateClients.ContainsKey(clientPrivateToken)) throw CometException.CometClientDoesNotExistException(); // // ok, get the client InProcCometClient cometClient = privateClients[clientPrivateToken]; List<long> toDelete = new List<long>(); List<long> toReturn = new List<long>(); // wicked, we have the client, so we can get its messages from our list // we delete any before the last messageId becuase we dont want them foreach(long key in cometClient.Messages.Keys) { if(key <= lastMessageId) toDelete.Add(key); else toReturn.Add(key); } // delete the ones from the messages foreach (long key in toDelete) { cometClient.Messages.Remove(key); } // and return the ones in the toReturn array List<CometMessage> cometMessages = new List<CometMessage>(); foreach (long key in toReturn) { cometMessages.Add(cometClient.Messages[key]); } return cometMessages.ToArray(); } } /// <summary> /// Send a message to a specific client /// </summary> /// <param name="clientPublicToken"></param> /// <param name="name"></param> /// <param name="contents"></param> public void SendMessage(string clientPublicToken, string name, object contents) { if (string.IsNullOrEmpty(clientPublicToken)) throw new ArgumentNullException("clientPublicToken"); if (contents == null) throw new ArgumentNullException("contents"); lock (state) { if (!publicClients.ContainsKey(clientPublicToken)) throw CometException.CometClientDoesNotExistException(); // // ok, get the client InProcCometClient cometClient = publicClients[clientPublicToken]; // ok, stick the message in the array CometMessage message = new CometMessage(); message.Contents = contents; message.Name = name; message.MessageId = cometClient.NextMessageId; // increment cometClient.NextMessageId++; cometClient.Messages.Add(message.MessageId, message); } } /// <summary> /// Send a message to all the clients /// </summary> /// <param name="name"></param> /// <param name="contents"></param> public void SendMessage(string name, object contents) { if (contents == null) throw new ArgumentNullException("contents"); lock (state) { foreach (InProcCometClient cometClient in publicClients.Values) { // ok, stick the message in the array CometMessage message = new CometMessage(); message.Contents = contents; message.Name = name; message.MessageId = cometClient.NextMessageId; // increment cometClient.NextMessageId++; cometClient.Messages.Add(message.MessageId, message); } } } /// <summary> /// Get the client from the state provider /// </summary> /// <param name="clientPrivateToken"></param> /// <returns></returns> public CometClient GetCometClient(string clientPrivateToken) { if (!this.privateClients.ContainsKey(clientPrivateToken)) throw CometException.CometClientDoesNotExistException(); // return the client private token return this.privateClients[clientPrivateToken].CometClient; } /// <summary> /// Remove an idle client from the memory /// </summary> /// <param name="clientPrivateToken"></param> public void KillIdleCometClient(string clientPrivateToken) { if (!this.privateClients.ContainsKey(clientPrivateToken)) throw CometException.CometClientDoesNotExistException(); // get the client InProcCometClient ipCometClient = this.privateClients[clientPrivateToken]; // and remove the dictionarys this.privateClients.Remove(ipCometClient.CometClient.PrivateToken); this.publicClients.Remove(ipCometClient.CometClient.PublicToken); } #endregion } }

 

PS:可以说这篇随笔中将COMET IM的几个最容易拓展的类进行了介绍,核心部分将在下一篇随笔中进行讲解,欢迎大家讨论并拍砖。同时预祝大家春节愉快!

转载于:https://www.cnblogs.com/lzlynn/archive/2009/01/21/1379575.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/363399.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何在JavaServer Pages中使用Salesforce REST API

摘要&#xff1a;本教程提供了一个JSP示例以及如何将其与Salesforce REST API集成。 我们将逐步完成创建外部客户端以使用Force.com &#xff08;同时使用HTTP&#xff08;S&#xff09;和JSON&#xff09;管理您的数据的分步过程。 在此示例中&#xff0c;我将Mac OS X 10.9.…

写在08年“愚人节”

从博客园学了很多东西&#xff0c;是时候该回馈了。谢谢博客园给我们提供这么好的平台交流技术。 刚发了文章习惯性的看了下自己博客的首页代码&#xff0c;感觉日历不够精良。 我做过的一个ajax日历&#xff0c;可以参考解放日报 艺术家具版的日期直达功能的日历&#xff0c;用…

JQuery实现点击按钮切换图片(附源码)--JQuery基础

JQuery实现切换图片相对比较简单&#xff0c;直接贴代码了哈&#xff0c;有注释噢&#xff01;疑问请追加评论哈&#xff0c;不足之处还请大佬们指出&#xff01; 1、案例代码&#xff1a; demo.html&#xff1a; <!DOCTYPE html><html><head>   <me…

CSS3盒子模型

web前端必须了解的CSS3盒子模型 1、需要了解的属性以及属性值 display:box或者display:inline-box box-orient:horizontal | vertical (水平 垂直) 定义盒模型的布局方向 box-direction:normal reverse(正序 反序) 元素排列顺序 box-ordinal-group:number(数值) 设置元素…

与JBoss Fuse,Jenkins和Nexus的持续集成

最近&#xff0c;我正在整理一个快速启动的Maven项目&#xff0c;以展示一种组织JBoss Fuse项目的可行方法。 该项目可在Github上找到&#xff1a; https &#xff1a; //github.com/paoloantinori/fuse_ci 这是我与朋友詹姆斯罗林斯 &#xff08; James Rawlings&#xff09…

html5表单与PHP交互

1、示例代码 前端&#xff1a; <!DOCTYPE html><html><head><meta charset"utf-8"> <title>html5表单与PHP交互</title></head><body><form action"http://localhost/jh.php" method"post"…

【DP】【期望】$P1850$换教室

【DP】【期望】\(P1850\)换教室 链接 题目描述 有 \(2n\) 节课程安排在$ n$ 个时间段上。在第 \(i\)&#xff08;\(1 \leq i \leq n\)&#xff09;个时间段上&#xff0c;两节内容相同的课程同时在不同的地点进行&#xff0c;其中&#xff0c;牛牛预先被安排在教室 \(c_i\)上课…

高并发服务器逻辑处理瓶颈,如何解决?

https://mp.weixin.qq.com/s/GHHHvgURdZpNJ1Ec6RHgPg 高并发衡量指标 响应时间&#xff1a;系统对请求做出响应的时间&#xff0c;即一个http请求返回所用的时间&#xff1b;吞吐量&#xff1a;单位时间内处理的请求数量&#xff1b;QPS&#xff08;TPS&#xff09;&#xff1a…

Java 8 StampedLocks与ReadWriteLocks和同步

同步部分就像访问您的岳父母。 您希望尽可能少出现。 关于锁定&#xff0c;规则是相同的–您想花费最短的时间在关键区域内获取锁定&#xff0c;以防止形成瓶颈。 锁定的核心语言惯用法一直是用于方法和离散块的synced关键字。 这个关键字实际上已硬连接到HotSpot JVM中。 我们…

MSN on 2/16/2009

转载于:https://www.cnblogs.com/zxlin25/archive/2009/02/16/1391207.html

开发微信小程序中SSL协议的申请、证书绑定、TLS 版本处理等

在上篇随笔《基于微信小程序的系统开发准备工作》介绍了开发微信小程序的一些前期的架构设计、技术路线 、工具准备等方面内容&#xff0c;本篇随笔继续这个步骤&#xff0c;逐步介绍我们实际开发过程中对SSL协议的申请及后期处理过程&#xff0c;包括证书的IIS端口绑定&#x…

【面向对象】对比JavaScript、Go、Ada、Python、C++、Java、PHP的访问限制。

在不同编程语言中&#xff0c;控制成员&#xff08;变量、方法、类等&#xff09;可见性的机制不尽相同。以下是对比JavaScript、Go、Ada、Python、C、Java、PHP所使用的访问限制关键字和约定&#xff1a; 一、JavaScript ### JavaScript访问限制 早期的JavaScript并没有类似…

Web API应用架构设计分析(1)

Web API 是一种应用接口框架&#xff0c;它能够构建HTTP服务以支撑更广泛的客户端&#xff08;包括浏览器&#xff0c;手机和平板电脑等移动设备&#xff09;的框架&#xff0c; ASP.NET Web API 是一种用于在 .NET Framework 上构建 RESTful 应用程序的理想平台。本文主要以AS…

MapXtreme2008中操作矢量符号和定制符号

本文部分说明内容摘自网络文章&#xff0c;经过本人在MapXtreme2008中编写相关的演示例子&#xff0c;详细说明如何操作MapXtreme2008提供的矢量符号和定制符号。 MapXtreme 在其安装过程中自动安装 10 种 MapInfo 特定的 TrueType 字体。这些字体为用户提供了字形符号选择&am…

转载:97特瑞心得

单位里无聊打着玩的心得&#xff0c;写了段时间了&#xff0c;基本是哪天想到什么就打上去&#xff0c;狗屁不通的地方请大家包涵。97特瑞玩了有10年多了吧,97刚出来的时候就玩的特瑞&#xff0c;别人都八神&#xff0c;萝卜特&#xff0c;克拉克的时代我就坚持用特瑞&#xff…

吸气剂/设定者。 邪恶。 期。

从2003年开始&#xff0c;艾伦霍鲁布&#xff08;Allen Holub&#xff09;讨论了为什么吸气剂和塞特方法是邪恶的著名文章&#xff0c;关于吸气剂/塞特方法是否是反模式&#xff0c;应该避免使用&#xff0c;还是我们在面向对象中不可避免地需要它&#xff0c;这是一个古老的争…

【原】.Net创建Excel文件(插入数据、修改格式、生成图表)的方法

1.添加Excel引用 可以在.Net选项卡下添加Microsoft.Office.Interop.Excel引用&#xff0c;或在COM下添加Microsoft Excel 12.0 Object Library。它们都会生成Microsoft.Office.Interop.Excel.dll。 2.创建Excel。 有两种方法创建一个Excel Workbook实例。 1.需要一个模板文件&…

求助:安装程序无法创建一个DCOM用户帐号来注册.....\valec.exe

http://support.microsoft.com/kb/257413/ 这是Visual Studio的一个BUG&#xff0c;只出现在Windows 2000/XP下。如果你不使用Visual Studio Analyzer&#xff0c;可以在安装时选择Custom&#xff0c;然后在Enterprise Tools中清除掉Visual Studio Analyzer。再安…

js中split()和join()的用法

Split()方法&#xff1a;把一个字符串分割成字符串数组 如上所示&#xff1a;把字符串a按空格分隔&#xff0c;得3个字符串数组。 在如&#xff1a; var a”hao are you” a.split(“”); 得到[h,a,o,a,r,e,y,o,u]; Join方法: 把数组中的所有元素转换为一个字符串 如上图所…

IT行业经典面试题,121套面试题

IT行业经典面试题&#xff0c;121套面试题 资源大小&#xff1a; 580.80KB资源类型&#xff1a;发布人&#xff1a; eyelife 发布日期&#xff1a; 2天前Tag&#xff1a; 名企,计算机 资源分&#xff1a; 10下载人数&#xff1a; 857 4.33/347人评分 12 3 4 5 评论 分享…