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
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的COMET彗星(三)构建自己的COMET核心的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决win2003安装exchangeS
- 下一篇: js左右对联