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. 【LeetCode从零单排】No15 3Sum

    称号 Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all ...

  2. UVa 208 - Firetruck 回溯+剪枝 数据

    题意:构造出一张图,给出一个点,字典序输出所有从1到该点的路径. 裸搜会超时的题目,其实题目的数据特地设计得让图稠密但起点和终点却不相连,所以直接搜索过去会超时. 只要判断下起点和终点能不能相连就行了 ...

  3. Debian 7设备nginx周围环境、编译并安装和启动

    /*********************************************************************  * Author  : Samson  * Date   ...

  4. Suse发生了错误Access denied for user &#39;&#39;@&#39;localhost&#39; to&amp;

    好久没实用MySQL了,上次由于装了Banq的论坛系统.在用MySQL Administrator进去的时候居然提示mysql error number 1045 access denied for ...

  5. PC结束 Spark 二次开发 收到自己主动,并允许好友请求

    本次Spark二次开发是为了客服模块的开发, 能让用户一旦点击该客服则直接自己主动加入好友.而客服放则需自己主动加入好友,不同弹出对话框进行允许,这方便的广大客服. 如今废话不多说,直接上代码. pa ...

  6. Scala刮:使用Intellij IDEA写hello world

    介绍 在前面的文章中,,我们介绍了如何使用Scala IDE那是,eclipse集成Scala开发插件Scala开发语言程序.使用一段时间后,.发现eclipse正确Scala支持不是很好.用户体验差 ...

  7. 看德日进,凯文·凯利与Kurzweil老师?

    生命从哪里来.要到那里去.生命存在的意义是什么.这些差点儿是人类可以探究的最深层次问题.基督教给出的答案是毁灭和审判.佛学给出的答案是无常,科学的达尔文进化论给出了生命的起点和进化的过程,对于未来.达 ...

  8. leetcode 刷道题 70 earch Insert Position 二进制搜索插入位置

    Given a sorted array and a target value, return the index if the target is found. If not, return the ...

  9. Mule ESB-Content-Based Routing Tutorial(2)

    承接 Mule ESB-Content-Based Routing Tutorial(1) 五.执行应用程序  完毕创建,配置.并保存你的新的应用程序,您就能够在嵌入Mule的server上执行(包含 ...

  10. thinkphp学习笔记2—入口文件

    原文:thinkphp学习笔记2-入口文件 在thinkphp中有两个入口文件,一个是项目的入口文件,是index.php在主目录里面,还有一个是thinkphp框架的的入口文件,放在框架目录下面如: ...