长连接实现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));
}
posted @ 2013-10-03 15:28  mimo0  阅读(352)  评论(0)    收藏  举报