WebIM组件设计<转>

简介:

其实对于一个WebIM来说,我们一般都几种方式来实现

1.服务器拉送方式:就是客户端主动定时的去服务器端获取信息,一般我们可以通过 Ajax控件+定时器  来实现,优点:实现简单,缺点:服务器承受的压力重

2.服务器推送方式:就是服务器主动将获取到的信息,发送给客户端,Asp.net我们可以通过 IHttpAsyncHandler这个接口来实现,优点:服务器承受的压力小 缺点:服务器 和客户端同时配合编写(js)

3.Flash Socket方式:通过在网页中嵌入Flash,编写Socket程序 通过AS 3.0语言,Socket编程,优点:并发量大,服务器压力小,缺点:浏览器中要支持Flash,加载Flash

下面我实现的是第二种: 

它实现的原理很简单,浏览器向服务器发送一个异步请求,服务器接到请求之后,去内存中查找信息,如果有,就直接处理该请求,如果没有,我们可以把该请求暂存到服务器一段时间,如果在该时间段内,有符合自己信息的到来,我们就直接处理,否则我们要丢掉该请求(其实请求丢掉之后,如果客户端任然在线,客户端会重新在次发送请求,就像是心跳包)

复制代码
 
 
using System.Collections.Generic;
using System.Collections;
using System.Text;
using System.Web;
namespace DNCCFrameWork.Chat
{
/// <summary>
/// Chat聊天异步处理程序
/// </summary>
public class ChatAsyncHandler : IHttpAsyncHandler
{

// 接受客户端请求
public IAsyncResult BeginProcessRequest(HttpContext context, AsyncCallback cb, Object extraData)
{
System.IO.Stream stream
= context.Request.InputStream;
byte [] b = ReadDate(stream);
string str = context.Server.UrlDecode(context.Request.ContentEncoding.GetString(b));
Hashtable ht
= SplitRequestPara(str);
string type = ht[ " type " ] as string ;
string message = ht[ " content " ] as string ;
string sendUser = ht[ " sendusers " ] as string ;
string receiveUser = ht[ " receiveusers " ] as string ;
IChatMessage chatMessage
= new ChatMessage();
chatMessage.Type
= type;
chatMessage.Content
= message;
chatMessage.SendUser
= sendUser;
chatMessage.ReceiveUser
= receiveUser;
ChatAsyncResult chatAsyncResult
= new ChatAsyncResult(context, cb, extraData, chatMessage);
IChat chat
= new ChatDefault();

chat.IM(chatAsyncResult);

return chatAsyncResult;

}

#region 读流中的全部数据
private byte [] ReadDate(System.IO.Stream stream)
{
byte [] buff = new byte [stream.Length];
int curOffset = 0 ;
int totalCount = 0 ;
int readCount = 0 ;
int size = buff.Length;
while (totalCount < size)
{
int exceptSize = size - totalCount;
readCount
= stream.Read(buff, curOffset, exceptSize);
curOffset
+= readCount;
totalCount
+= readCount;
}
return buff;
}
#endregion

#region 分离请求数据
private Hashtable SplitRequestPara( string str)
{
Hashtable ht
= new Hashtable();
string [] str1 = str.Split( new char [] { ' & ' });
foreach ( string temp in str1)
{
string [] str2 = temp.Split( new char [] { ' = ' });
if (str2.Length == 2 )
{
if ( ! ht.ContainsKey(str2[ 0 ].ToLower().ToString()))
{
ht.Add(str2[
0 ].ToLower().ToString(), str2[ 1 ].ToString());
}
}
}
return ht;

}
#endregion

public void EndProcessRequest(IAsyncResult result)
{

}

#region IHttpHandler接口
public bool IsReusable
{
get { return false ; }
}
public void ProcessRequest(HttpContext context)
{

}
#endregion

#region 请求检测
private bool RequestCheck(IChatMessage cm)
{
bool status = true ;
if ( string .IsNullOrEmpty(cm.Type))
{
status
= false ;

}
else
{
try
{
cm.Type
= Common.Security.CryptoTextBase.ProcessText(cm.Type, false );
}
catch
{
status
= false ;
}
}
if ( string .IsNullOrEmpty(cm.SendUser))
{
status
= false ;
}
if ( string .IsNullOrEmpty(cm.ReceiveUser))
{
status
= false ;
}
return status;
}
#endregion

}
}
复制代码

消息处理中心

复制代码
 
 
using System;
using System.Collections.Generic;
using System.Text;
using System.Collections;
namespace DNCCFrameWork.Chat
{
public class ChatMessageDispatcherDefault : IChatMessageDispatcher
{
private IChatMessageSendStrategy m_chatMessageSendStrategy = new ChatMessageSendStrategyDefault();
private IChatMessageHook m_chatMessageHook = new ChatMessageHookDefault();
private IChatMessageSpy m_chatMessageSpy = new ChatMessageSpyDefault();
private IChatMessageDeal m_chatMessageDeal;
private IChatMessageManager m_chatMessageManager = new ChatMessageManagerDefault();
private IChatRequestManager m_chatRequestManager = new ChatRequestManagerDefault();
private static object o = new object ();
public void Dispatcher(ChatAsyncResult chatAsyncResult)
{
ChatAsyncResult car
= Receive(chatAsyncResult); // 接受请求
m_chatMessageDeal = ChatMessageDealFactory.GetChatMessageDeal(car); // 获取请求对应的处理器
if (m_chatMessageDeal != null )
{
m_chatMessageDeal.Deal(car);
// 处理器处理消息,保存消息,或保存请求
lock (o)
{
DispatcherSend();
// 发送消息
}
}
}
public void DispatcherSend()
{
int count = m_chatRequestManager.Count();
for ( int i = 0 ; i < count; i ++ )
{
IChatRequest chatRequest
= m_chatRequestManager.Pop();
if (chatRequest != null )
{
Dictionary
< string , string > dic = new Dictionary < string , string > ();
dic.Add(
" SendUser " , chatRequest.ChatAsyncResult.ChatMessage.SendUser);
dic.Add(
" ReceiveUser " , chatRequest.ChatAsyncResult.ChatMessage.ReceiveUser);
dic.Add(
" Type " , chatRequest.ChatAsyncResult.ChatMessage.Type);
IList lt
= m_chatMessageManager.Find(dic);
if (lt.Count <= 0 )
{
TimeSpan ts
= DateTime.Now - chatRequest.ChatAsyncResult.ChatMessage.CreateDate;
if (ts.TotalSeconds < 600 ) // 请求超时系统丢弃请求
{
m_chatRequestManager.Push(chatRequest);
}
}
else
{
IList temp
= new ArrayList(); ;
foreach (IChatMessage chatMessage in lt)
{
IChatMessage cm
= Send(chatMessage);
temp.Add(cm);
// 发送消息前

}
StringBuilder sb
= new StringBuilder();
for ( int j = 0 ; j < temp.Count; j ++ )
{
IChatMessage cha
= temp[j] as IChatMessage;
sb.Append(cha.ToString()).Append(
" <br/> " );
}
chatRequest.ChatAsyncResult.ChatMessage.Content
= sb.ToString();
m_chatMessageSendStrategy.Send(
null , chatRequest);
}
}

}
}
public IChatMessage Send(IChatMessage chatAsyncResult)
{
IChatMessage car
= m_chatMessageHook.Send(chatAsyncResult);
car
= m_chatMessageSpy.Send(car);
return car;
}
public ChatAsyncResult Receive(ChatAsyncResult chatAsyncResult)
{
ChatAsyncResult car
= m_chatMessageHook.Receive(chatAsyncResult);
car
= m_chatMessageSpy.Receive(car);
return car;
}
}
}
复制代码

内存存储介质

复制代码
 
 
using System;

using System.Collections.Generic;
using System.Text;
using System.Collections;
using System.Configuration;
namespace DNCCFrameWork.Chat
{
public class ChatMessageStoreageMemory : IChatMessageStorage
{
private static Dictionary < string , LinkedList < IChatMessage >> m_SendUserReceiveChatMessage = new Dictionary < string , LinkedList < IChatMessage >> ();
private static ChatMessageStoreageDataBase db = new ChatMessageStoreageDataBase();
private static Dictionary < string , object > m_Lock = new Dictionary < string , object > ();
private static int m_ChatGroupALiveTime = 5 ;
private static int m_ChatMessageGC = 5 ;
private static DateTime m_LastChatMessageGC = DateTime.Now;
private static System.Timers.Timer GC = new System.Timers.Timer(m_ChatMessageGC * 60 * 1000 );
static ChatMessageStoreageMemory()
{
IList lt
= db.Find(); // 获取数据库中为发送信息
foreach (IChatMessage chatMessage in lt)
{
BackChatMessage(chatMessage);
}
int .TryParse(ConfigurationManager.AppSettings[ " ChatGroupALiveTime " ], out m_ChatGroupALiveTime);
int .TryParse(ConfigurationManager.AppSettings[ " ChatMessageGC " ], out m_ChatMessageGC);
GC.Interval
= m_ChatMessageGC * 60 * 1000 ;
GC.Elapsed
+= new System.Timers.ElapsedEventHandler(GC_Elapsed);
GC.Enabled
= true ;
GC.Start();
}

static void GC_Elapsed( object sender, System.Timers.ElapsedEventArgs e)
{
ChatMessageGC();
}
private static void BackChatMessage(IChatMessage chatMessage)
{
string key = "" ;
string key2 = "" ;
if (chatMessage.Type.Equals(ChatType.oneTonoeSend))
{
key
= chatMessage.SendUser + " <--> " + chatMessage.ReceiveUser;
key2
= chatMessage.ReceiveUser + " <--> " + chatMessage.SendUser;
}
if (chatMessage.Type.Equals(ChatType.manyTomanySend))
{
key
= key2 = chatMessage.ReceiveUser;
}
if ( ! m_SendUserReceiveChatMessage.ContainsKey(key) || ! m_SendUserReceiveChatMessage.ContainsKey(key2))
{
lock (m_SendUserReceiveChatMessage)
{
if ( ! m_SendUserReceiveChatMessage.ContainsKey(key) && ! m_SendUserReceiveChatMessage.ContainsKey(key2))
{
m_SendUserReceiveChatMessage.Add(key,
new LinkedList < IChatMessage > ());
m_Lock.Add(key,
new object ());
}
}

}
if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
lock (m_Lock[key])
{
if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
if (m_SendUserReceiveChatMessage[key].Count >= 1 )
{
bool status = false ;
IChatMessage temp
= null ;
foreach (IChatMessage cm in m_SendUserReceiveChatMessage[key])
{
if (cm.CreateDate > chatMessage.CreateDate)
{
temp
= cm;
status
= true ; break ;
}
}
if (status)
{
m_SendUserReceiveChatMessage[key].AddBefore(m_SendUserReceiveChatMessage[key].Find(temp), chatMessage);
}
if ( ! status)
{
m_SendUserReceiveChatMessage[key].AddLast(chatMessage);
}
}
else
{
m_SendUserReceiveChatMessage[key].AddFirst(chatMessage);
}
}
}
}
else
{
lock (m_Lock[key2])
{
if (m_SendUserReceiveChatMessage.ContainsKey(key2))
{
if (m_SendUserReceiveChatMessage[key2].Count >= 1 )
{
bool status = false ;
IChatMessage temp
= null ;
foreach (IChatMessage cm in m_SendUserReceiveChatMessage[key2])
{
if (cm.CreateDate > chatMessage.CreateDate)
{
temp
= cm;
status
= true ;
break ;
}
}
if (status)
{
m_SendUserReceiveChatMessage[key2].AddBefore(m_SendUserReceiveChatMessage[key2].Find(temp), chatMessage);
}
if ( ! status)
{
m_SendUserReceiveChatMessage[key2].AddLast(chatMessage);
}
}
else
{
m_SendUserReceiveChatMessage[key2].AddFirst(chatMessage);
}
}
}
}
}
public void Add(IChatMessage chatMessage)
{
string key = "" ;
string key2 = "" ;
if (chatMessage.Type.Equals(ChatType.oneTonoeSend))
{
key
= chatMessage.SendUser + " <--> " + chatMessage.ReceiveUser;
key2
= chatMessage.ReceiveUser + " <--> " + chatMessage.SendUser;
}
if (chatMessage.Type.Equals(ChatType.manyTomanySend))
{
key
= key2 = chatMessage.ReceiveUser;
}
if ( ! m_SendUserReceiveChatMessage.ContainsKey(key) || ! m_SendUserReceiveChatMessage.ContainsKey(key2))
{
lock (m_SendUserReceiveChatMessage)
{
if ( ! m_SendUserReceiveChatMessage.ContainsKey(key) && ! m_SendUserReceiveChatMessage.ContainsKey(key2))
{
m_SendUserReceiveChatMessage.Add(key,
new LinkedList < IChatMessage > ());
m_Lock.Add(key,
new object ());
}
}

}
if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
lock (m_Lock[key])
{
if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
if (m_SendUserReceiveChatMessage[key].Count >= 1 )
{
bool status = false ;
IChatMessage temp
= null ;
foreach (IChatMessage cm in m_SendUserReceiveChatMessage[key])
{
if (cm.CreateDate > chatMessage.CreateDate)
{
temp
= cm;
status
= true ; break ;
}
}
if (status)
{
m_SendUserReceiveChatMessage[key].AddBefore(m_SendUserReceiveChatMessage[key].Find(temp), chatMessage);
}
if ( ! status)
{
m_SendUserReceiveChatMessage[key].AddLast(chatMessage);
}
}
else
{
m_SendUserReceiveChatMessage[key].AddFirst(chatMessage);
}
cb c
= new cb(db.Add);
c.BeginInvoke(chatMessage,
null , null ); // 异步写入数据库
}
}
}
else
{
lock (m_Lock[key2])
{
if (m_SendUserReceiveChatMessage.ContainsKey(key2))
{
if (m_SendUserReceiveChatMessage[key2].Count >= 1 )
{
bool status = false ;
IChatMessage temp
= null ;
foreach (IChatMessage cm in m_SendUserReceiveChatMessage[key2])
{
if (cm.CreateDate > chatMessage.CreateDate)
{
temp
= cm;
status
= true ;
break ;
}
}
if (status)
{
m_SendUserReceiveChatMessage[key2].AddBefore(m_SendUserReceiveChatMessage[key2].Find(temp), chatMessage);
}
if ( ! status)
{
m_SendUserReceiveChatMessage[key2].AddLast(chatMessage);
}
}
else
{
m_SendUserReceiveChatMessage[key2].AddFirst(chatMessage);
}
cb c
= new cb(db.Add);
c.BeginInvoke(chatMessage,
null , null ); // 写入数据库
}
}
}
}
public IList Find(Dictionary < string , string > dic)
{

string key = "" ;
if (dic[ " Type " ].Equals(ChatType.oneTooneReceive))
{
key
= dic[ " SendUser " ] + " <--> " + dic[ " ReceiveUser " ];
}
if (dic[ " Type " ].Equals(ChatType.manyTomanyReceive))
{
key
= dic[ " ReceiveUser " ];
}
IList lt
= new ArrayList();

if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
lock (m_Lock[key])
{
if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
foreach (IChatMessage chatMessage in m_SendUserReceiveChatMessage[key])
{
if ( ! chatMessage.IsSend(dic[ " SendUser " ]) && ! chatMessage.IsOver)
{
chatMessage.SendUserList.Add(dic[
" SendUser " ]);
chatMessage.ReadDateList.Add(DateTime.Now.ToString());
ChatMessageOverCheck(chatMessage);
cb c
= new cb(db.Update);
c.BeginInvoke(chatMessage.Clone(),
null , null ); // 异步更新数据库
lt.Add(chatMessage);
}
}
}
}
}
else
{
if (dic[ " Type " ].Equals(ChatType.oneTooneReceive))
{
key
= dic[ " ReceiveUser " ] + " <--> " + dic[ " SendUser " ];
}
if (dic[ " Type " ].Equals(ChatType.manyTomanyReceive))
{
key
= dic[ " ReceiveUser " ];
}
if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
lock (m_Lock[key])
{
if (m_SendUserReceiveChatMessage.ContainsKey(key))
{
foreach (IChatMessage chatMessage in m_SendUserReceiveChatMessage[key])
{
if ( ! chatMessage.IsSend(dic[ " SendUser " ]) && ! chatMessage.IsOver)
{
chatMessage.SendUserList.Add(dic[
" SendUser " ]);
chatMessage.ReadDateList.Add(DateTime.Now.ToString());
ChatMessageOverCheck(chatMessage);
cb c
= new cb(db.Update);
c.BeginInvoke(chatMessage.Clone(),
null , null ); // 异步更新数据库
lt.Add(chatMessage);
}
}
}
}
}
}
return lt;

}
public void Clear(Dictionary < string , string > dic)
{
}
public void Clear(IList lt)
{

}

#region 定时内存清理
private static void ChatMessageGC()
{
foreach (KeyValuePair < string , LinkedList < IChatMessage >> kvp in m_SendUserReceiveChatMessage)
{
if (kvp.Value.Count > 0 )
{
lock (m_Lock[kvp.Key])
{
if (kvp.Value.Count > 0 )
{
IList lt
= new ArrayList();
foreach (IChatMessage cm in kvp.Value)
{
if ( ! cm.IsOver)
{
ChatMessageOverCheck(cm);
}
if (cm.IsOver)
{
lt.Add(cm);
}
}
foreach (IChatMessage cm in lt)
{
kvp.Value.Remove(cm);
cb b
= new cb(db.Add2);
b.BeginInvoke(cm.Clone(),
null , null ); // 进入历史数据库
}
}
}
}


}

}
#endregion

#region 消息完成结束检测
private static void ChatMessageOverCheck(IChatMessage cm)
{
if (cm.Type.Equals(ChatType.oneTonoeSend) && cm.SendUserList.Count >= 2 )
{
cm.OverDate
= DateTime.Now;
cm.IsOver
= true ;
}
if (cm.Type.Equals(ChatType.manyTomanySend))
{
TimeSpan ts
= DateTime.Now - cm.CreateDate;
if (ts.TotalMinutes > m_ChatGroupALiveTime)
{
cm.OverDate
= DateTime.Now;
cm.IsOver
= true ;
}
}
}
#endregion
}
public delegate void cb(IChatMessage chatMessage);
}
复制代码

所有的消息都先存储在内存中,然后异步更新到数据库中(相当于内存快照,一旦应用程序重启,没有被处理完过的消息,将重新加载到内存中),一旦消息被处理过(一对一聊天,双方都接受到)则消息标记为已处理过,消息就会从内存中清除,同时被异步更新到历史库表中 

 JS部分:

客户端异步发请求
  

复制代码
 
 
< script src = " ../Js/jquery-1.4.3.js " type = " text/javascript " language = " javascript " ></ script >

< script type = " text/javascript " >
$(document).ready(function () {

function send()
{
var str
= $.trim($( " #ctl00_ContentPlaceHolder1_tbMSGInput " ).val());
var sendUser
= $.trim( ' <% = MyUid %> ' );
var receiveUser
= $.trim( ' <% = ToUID %> ' );
if (sendUser.length < 1 && receive.length < 1 )
{
return ;
}
if (str.length > 1 )
{
$.ajax({
type:
" post " ,
url:
" IM.aspx " ,
timeout:
30000 ,
data:{ content: str, type:
" 0 " , sendUsers: sendUser, receiveUsers: receiveUser } ,
dataType:
" html " ,
success: function (data, status)
{
if (data != " 发送成功 " )
{
var result
= $( " #ctl00_ContentPlaceHolder1_lbChatShow " );
result.html(result.html()
+ data + " <br/> " );
}
else
{
$(
" #ctl00_ContentPlaceHolder1_tbMSGInput " ).val( "" );
}

},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
var result
= $( " #ctl00_ContentPlaceHolder1_lbChatShow " );
result.html(result.html()
+ textStatus + " <br/> " );
}
});
$(
" #ctl00_ContentPlaceHolder1_tbMSGInput " ).val( "" );
}
}

function wait()
{
var sendUser
= $.trim( ' <% = MyUid %> ' );
var receiveUser
= $.trim( ' <% = ToUID %> ' );
if (sendUser.length < 1 && receive.length < 1 )
{
return ;
}
$.ajax({
type:
" post " ,
url:
" IM.aspx " ,
timeout:
30000 ,
data:{ content:
"" , type: " 1 " , sendUsers: sendUser, receiveUsers: receiveUser } ,
dataType:
" html " ,
success:function (data, status)
{
var result
= $( " #ctl00_ContentPlaceHolder1_lbChatShow " );
result.html(result.html()
+ data);
var outerHeight
= $( " #ctl00_ContentPlaceHolder1_lbChatShow " ).outerHeight();
var innerHeight
= $( " #div_Message " ).innerHeight();
var scrollTop
= outerHeight - innerHeight + 20 ;
$(
" #div_Message " ).scrollTop(scrollTop);
wait();
},
error: function(XMLHttpRequest, textStatus, errorThrown)
{
wait();
}
});
};

// 初始化连接
wait();
$(
" #btnSend " ).click(function () { send(); });
$(
" #ctl00_ContentPlaceHolder1_tbMSGInput " ).keypress(function ( event ) {
if ( event .keyCode == 13 ) {
send();
}
});
});
</ script >
复制代码

 部署方式:

在Web.config httpHandlers中加入即可

 <add path="IM.aspx" type="DNCCFrameWork.Chat.ChatAsyncHandler" verb="POST,Get"/>

相关文章
|
8月前
|
消息中间件 存储 缓存
如何设计各个组件之间的交互行为?
如何设计各个组件之间的交互行为?
|
开发者 容器
ArkUI框架,Flex布局,基础组件及封装,父子组件的双向绑定
Flex组件用于创建弹性布局,开发者可以通过Flex的接口创建容器组件,进而对容器内的其他元素进行弹性布局,例如:使三个元素在容器内水平居中,垂直等间隔分散。 例如:如下的布局代码分别表示:(垂直排列,水平居中,垂直居中)
340 0
ArkUI框架,Flex布局,基础组件及封装,父子组件的双向绑定
|
5月前
|
开发框架 小程序 JavaScript
小程序框架->框架,视图层,生命周期(逻辑层)
小程序框架->框架,视图层,生命周期(逻辑层)
30 0
|
6月前
|
前端开发
如何理解受控和非受控件组件?
如何理解受控和非受控件组件?
|
12月前
|
人工智能 JavaScript 前端开发
如何开发一个人人爱的组件?
本篇文章类似一个菜谱,比较零碎的记录一些组件设计的内容,作者分别按照 1~5 星 区分其重要性。
6078 1
|
前端开发
前端学习案例1-受控组件和非受控组件1
前端学习案例1-受控组件和非受控组件1
43 0
前端学习案例1-受控组件和非受控组件1
|
Java 关系型数据库 程序员
【组件设计开发】采用领域驱动设计设计和开发可组装的组件
采用领域驱动设计设计和开发可组装的组件
27855 7
【组件设计开发】采用领域驱动设计设计和开发可组装的组件
|
JSON 前端开发 API
前端组件模块化设计思路-何飞
在整个前端代码的开发中,经常会不停的造轮子,写重复的代码,导致整体的研发成本过多,开发效率变慢,而在如今低代码盛行的时代,如何降低研发成本,提升开发效率成了很多大厂探索的目标,目前市面上大多数的组件库(Ant Design、ElementUI 等)都是能给开发带来便利,但是不具备业务属性,还是需要通过组件库的能力进行上层的搭建,无法满足业务需求的变化,从而导致研发成本的增加。
|
前端开发 API UED
「前端UI组件」如何实现一个骨架屏组件
用技术实现梦想,用梦想打开前端技术之门。今天分享如何实现一个骨架屏组件。
507 1
「前端UI组件」如何实现一个骨架屏组件
|
JavaScript
使用组件的细节点
《Vue实战笔记》
67 1