WebIM系列文章

之前笔者发布的云翔在线软件平台中已经包含了一个功能相对比较齐全的WebIM,这个系列的文章就是介绍如何开发出功能类似的WebIM,在文章开始前,先介绍一下相关的技术:

1.Comet

Comet 是一种新的 Web 应用架构。基于这种架构开发的应用中,服务器端会主动以异步的方式向客户端程序推送数据,而不需要客户端显式的发出请求。Comet 架构非常适合事件驱动的 Web 应用,以及对交互性和实时性要求很强的应用,如股票交易行情分析、聊天室和 Web 版在线游戏等。

在.NET要实现Comet就要用到IHttpAsyncHandler,在开始阅读文章前,建议先了解一下IHttpAsyncHandler。

2.Lesktop

Lesktop是一款用于开发RIA网站的开源JS界面库,Lesktop提供了一个功能强大的可视化开发工具帮助您快速的开发RIA网站。这个系列介绍的WebIM的前台UI将使用Lesktop来开发。

接下来,将开始今天的主题,开发一个简单的WebIM,这个WebIM将使用Comet技术,从而避免在客户端和服务端轮询,提高WebIM的性能(目前主要实现能够聊天,其他功能会在以后不断完善)。客户端界面在这就不详细介绍了,用Lesktop拖拖控件就可以了,效果如下:

1.基本思路

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));
}

文章来自:http://www.cnblogs.com/lucc/archive/2010/04/24/1719397.html

WebIM(1)的更多相关文章

  1. WebIM(5)----将WebIM嵌入到页面中

    在之前的文章中,已经开发了一个简单的WebIM,但是这个WebIM是在独立的页面中的,今天发布的WebIM是一个可以嵌入到自己网页中的版本,你只需添加少量的代码,就可以在页面中嵌入一个WebIM.不过 ...

  2. WebIM(4)----Comet的特殊之处

    WebIM系列文章 在一步一步打造WebIM(1)一文中已经使用Comet实现了一个简单的WebIM,那么,Comet究竟和一般的打开网页有何区别,本文将通过编写一个简单的HTTP服务器来说明两者的区 ...

  3. WebIM(3)----性能测试

    WebIM系列文章 在一步一步打造WebIM(1)和(2)中,已经讨论了如何开发一个WebIM,并且使用缓存来提高WebIM的性能,本文将编写一个程序模拟大量用户登录来对WebIM进行性能测试. 1. ...

  4. WebIM(2)---消息缓存

    WebIM系列文章 在一步一步打造WebIM(1)一文中,已经介绍了如何实现一个简单的WebIM,但是,这个WebIM有一个问题,就是每一次添加消息监听器时,都必须访问一次数据库去查询是否有消息,显然 ...

  5. angular整合环信webIM

    此处有两大坑: 1.下载easemob-websdk此npm包时,并没有下载strophe.js.crypto-js.underscore这三个包,需要自己手动下载. 2.如下方标红位置所示,需要自己 ...

  6. react 调用webIm

    记录下遇到的问题,之前引用腾讯云的webim,一直出错,现在改好了, 引用了, 以上是在public下的index.html引用, 但是在子模块console.log(webim);会报这个错 解决也 ...

  7. 腾讯云通信WebIM事件回调的坑~

    最近在开过工作中用到了腾讯IM的功能,由于业务的需要主要使用到了: 1.loginInfo 用户登录,用户信息 2.getRecentContactList 获得最近联系人 3.getLastGrou ...

  8. 使用springboot+layim+websocket实现webim

    使用springboot+layim+websocket实现webim 小白技术社   项目介绍 采用springboot和layim构建webim,使用websocket作为通讯协议,目前已经能够正 ...

  9. vue-cli3.0 Typescript 项目集成环信WebIM 群组聊天

    项目背景 环信webim 官方没有vue版本的,自己就根据sdk重写了个vue版本的,只实现了基础的 登录 群组功能,其他的可以根据需要参考官方文档,添加相应的功能. 环信webim SDK相关文档: ...

随机推荐

  1. JAVA —— console输入输出

    import java.io.*; public class ConsoleIOTest { public static void main(String[] args) { BufferedRead ...

  2. WebGL 支持测试,并已支持的浏览器版本摘要

    WebGL 支持情况检測与已支持浏览器版本号汇总 太阳火神的漂亮人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公 ...

  3. REST|RESTful初步认识

    工作中要用到jersey来实现restful风格的webservice.对于webservice另一定的认知(能够觉得是一种服务,远程调用的组件),可是对于restful笔者根本就木有了解过,rest ...

  4. ThreadLocal的内存泄露(转)

    ThreadLocal的目的就是为每一个使用ThreadLocal的线程都提供一个值,让该值和使用它的线程绑定,当然每一个线程都可以独立地改变它绑定的值.如果需要隔离多个线程之间的共享冲突,可以使用T ...

  5. TDD和BDD

    开发人员看测试之TDD和BDD   前言: 已经数月没有来园子了,写博客贵在坚持,一旦松懈了,断掉了,就很难再拾起来.但是每每看到自己博客里的博文的浏览量每天都在增加,都在无形当中给了我继续写博客的动 ...

  6. Java内存模型-jsr133规范介绍(转)

    最近在看<深入理解Java虚拟机:JVM高级特性与最佳实践>讲到了线程相关的细节知识,里面讲述了关于java内存模型,也就是jsr 133定义的规范. 系统的看了jsr 133规范的前面几 ...

  7. EXCEL Pivot table manipulate

    Add filter For the Demo time,I would like to filter out the products which not in Red and Black colo ...

  8. seaJs组建库

    seaJs组建库的使用   原文地址:seaJs学习笔记2 – seaJs组建库的使用 我觉得学习新东西并不是会使用它就够了的,会使用仅仅代表你看懂了,理解了,二不代表你深入了,彻悟了它的精髓. 所以 ...

  9. 动态传递参数到DevExpress.XtraReports的小结

    原文:动态传递参数到DevExpress.XtraReports的小结 前两种方法和WinForm一样,可以传递参数.数组.实体对象.DataTable等1. 采用构造函数具体用法:在Report中p ...

  10. XP下类似%windir% %userprofile% 的变量的说明(转)

    在一些批处理或者系统技巧操作教程文章中,我们常常会看到一些形如 %windir% 或者 %systemdrive% 的变量.这些变量都代表着什么含义呢?下面小技巧之家为大家整理了在Windows XP ...