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"/>

posted @ 2011-03-16 09:44  温景良(Jason)  Views(578)  Comments(0Edit  收藏  举报