长连接实现Comet(转)
Comet 便是指服务器推技术。它的实现方式是在浏览器与服务器之间建立一个长连接,待获得消息之后立即返回。否则持续等待,直至超时。客户端得到消息或超时之后,又会立即建立另一个长连接。 Comet技术的最大优势,自然就是很高的即使性。在.NET中实现这种方式并不困难,用IHttpAsyncHandler即可。
发送消息和添加监听器将由一个类型为MessageManagement对象来负责,
添加监听器代码如下:
/// <summary> /// 添加消息监听器,如果查找到符合监听器条件的消息,返回false,此时不会添加监听器 /// 如果没有查找到符合监听器条件的消息,返回true,此时监听器将被添加到m_Listeners中 /// </summary> public bool AddListener( String receiver, String sender, Nullable < DateTime > from, WebIM_AsyncResult asynResult) { MessageListener listener = new MessageListener (receiver, sender, from, asynResult); lock (m_Lock) { if (!m_Listeners.ContainsKey(receiver)) { m_Listeners.Add(receiver, new List < MessageListener >()); } List < MessageListener > listeners = m_Listeners[receiver] as List < MessageListener >; //查找消息 List < Message > messages = Find(receiver, sender, from); if (messages.Count == 0) { //插入监听器 listeners.Add(listener); } else { //发送消息 listener.Send(messages); } return messages.Count == 0; } }
发送消息代码如下:
/// <summary> /// 插入新的消息,插入消息后将查询m_Listeners中是否有符合条件的监听器,如存在,同时将消息发送出去 /// </summary> public Message NewMessage( String receiver, String sender, DateTime createdTime, String content) { lock (m_Lock) { Message message = new Message (sender, receiver, content, createdTime, ++m_MaxKey); SQLiteCommand cmd = new SQLiteCommand ( "insert into Message (Receiver,Sender,Content,CreatedTime,Key) values (?,?,?,?,?)" , m_Conn ); cmd.Parameters.Add( "Receiver" , DbType .String).Value = message.Receiver; cmd.Parameters.Add( "Sender" , DbType .String).Value = message.Sender; cmd.Parameters.Add( "Content" , DbType .String).Value = message.Content; cmd.Parameters.Add( "CreatedTime" , DbType .DateTime).Value = message.CreatedTime; cmd.Parameters.Add( "Key" , DbType .Int64).Value = message.Key; cmd.ExecuteNonQuery(); List < Message > messages = new List < Message >(); messages.Add(message); if (m_Listeners.ContainsKey(receiver)) { List < MessageListener > listeners = m_Listeners[receiver] as List < MessageListener >; List < MessageListener > removeListeners = new List < MessageListener >(); foreach ( MessageListener listener in listeners) { if ((listener.Sender == "*" || String .Compare(listener.Sender, sender, true ) == 0) && (listener.From == null || message.CreatedTime > listener.From)) { listener.Send(messages); removeListeners.Add(listener); System.Threading. ThreadPool .QueueUserWorkItem( new System.Threading. WaitCallback (listener.Complete)); } } foreach ( MessageListener listener in removeListeners) { //移除监听器 listeners.Remove(listener); } } return message; } }
2.使用IHttpAsyncHandler实现Comet
IHttpAsyncHandler的介绍可以查阅下msdn,以下是接收消息的源代码,主要是重写BeginProcessRequest和EndProcessRequest:
public class WebIM_ReceiveHandler : IHttpAsyncHandler { public WebIM_ReceiveHandler() { } HttpContext m_Context = null ; IAsyncResult IHttpAsyncHandler .BeginProcessRequest( HttpContext context, AsyncCallback cb, Object extraData) { m_Context = context; System.IO. Stream inputStream = context.Request.InputStream; Byte [] buffer = new Byte [inputStream.Length]; inputStream.Read(buffer, 0, ( int )inputStream.Length); string content = context.Request.ContentEncoding.GetString(buffer); Hashtable data = Utility .ParseJson(content) as Hashtable ; WebIM_AsyncResult asyncResult = new WebIM_AsyncResult (cb, extraData); Nullable < DateTime > from = data.ContainsKey( "From" ) ? new Nullable < DateTime >(( DateTime )data[ "From" ]) : null ; if (! MessageManagement .Instance.AddListener(data[ "Receiver" ] as string , data[ "Sender" ] as string , from, asyncResult)) { //已有消息,发送消息并结束链接 asyncResult.Complete( null ); } return asyncResult; } void IHttpAsyncHandler .EndProcessRequest( IAsyncResult result) { //将消息发送到客户端 WebIM_AsyncResult asyncResult = result as WebIM_AsyncResult ; asyncResult.Send(m_Context); } void IHttpHandler .ProcessRequest( HttpContext context) { } bool IHttpHandler .IsReusable { get { return true ; } } } public class WebIM_AsyncResult : IAsyncResult { AsyncCallback m_AsyncCallback = null ; object m_Data = null ; bool m_IsCompleted = false ; public WebIM_AsyncResult( AsyncCallback callback, Object extraData) { m_Data = extraData; m_AsyncCallback = callback; } bool IAsyncResult .IsCompleted { get { return m_IsCompleted; } } bool IAsyncResult .CompletedSynchronously { get { return false ; } } WaitHandle IAsyncResult .AsyncWaitHandle { get { return null ; } } Object IAsyncResult .AsyncState { get { return m_Data; } } StringBuilder m_Cache = new StringBuilder (); public void Write( object content) { m_Cache.Append(content.ToString()); } public void Send( HttpContext context) { context.Response.Write(m_Cache.ToString()); } public void Complete( object data) { m_AsyncCallback( this ); m_IsCompleted = true ; } }
3.客户端接收消息
客户端接收消息并不复杂,只需要发送请求,返回后在发送另一个请求即可,代码如下:
function Receive() { var data = { Receiver: User, Sender: Peer, From: m_From }; function Receive_Error(ex) { alert(ex); m_ErrorCount++; if (m_ErrorCount < 5) { //发送下一个请求 setTimeout(Receive, 1000); } } function Receive_Callback(xml, text) { m_ErrorCount = 0; //将JSON转成数据 var ret = System.ParseJson(text); //显示消息 for ( var i in ret.Messages) { m_MsgPanel.AddMessage(ret.Messages[i]); } if (ret.Messages.length > 0) { m_From = ret.Messages[ret.Messages.length - 1].CreatedTime; } //发送下一个请求 setTimeout(Receive, 50); } System.Post(Receive_Callback, Receive_Error, "recevie.aspx" , System.RenderJson(data)); }

浙公网安备 33010602011771号