距离上次写大厅已经有几个月。中间工作繁忙,待学习的东西又很多,又有很多新产品的想法想实践,一直把这个坑闲置着。
直到前两天简单的树遍历枚举器-挑战一个程序员到底能多懒 装配脑袋的几个回帖给我带来一些灵感,让我又重新看看我聊天频道的实现,发现竟然有很多可以推敲重新处理的地方。
昨天上班的时候又把聊天部分整理了下,愿和大家分享。
Wiki定义的聊天室
网络聊天室通常直称聊天室,是一种人们可以在线交谈的的网络论坛,在同一聊天室的人们通过广播消息进行实时交谈。
聊天室可以建立在即时通讯软件(如MSN Messenger、QQ)、P2P软件、万维网(如 Halapo, Meebo ) 等基础上,万维网方式更为普通和种类繁多,交谈的手段不局限于文本,更包括语音、视频。通常聊天室是按照房间或频道为单位的,在同一房间或频道的网人可以实时地广播和阅读公开消息。一般情况下,与其它网络论坛、即时通讯不同的是,聊天室不保存聊天记录。
聊天室的基础功能定义
用户能够通过某种形式连接到服务, 发送自己的信息给同样使用该服务的多人看到,并且看到同样使用该服务的别人发送的信息。
聊天室的行为
一般的聊天室行为分为两种
“推” 和 “拉”
推聊天室
推聊天室的行为接近一个中转点,或者说是一个广播站。
这种聊天室简单明确。
由于根本不需要保存信息 不会存在多个线程访问的临界数据,不会涉及到锁 也就不会产生相应的性能损耗。
缺点也显而易见,它仅仅支持服务器可以直接向客户端推送的连接(比如tcp, udp) ,http这种无连接的协议无法使用。如果是临时掉线,那么在重连前的所有数据也都全都接收不到。
变形品种 点对点聊天室
服务器把推送的义务转交给客户端。
这种方式对用户发送的内容控制力为0,如果不是tencent这种大牛 在伟大的社会主义初级阶段的和谐社会里 你执还意作这类点对点聊天室,建议请个好律师
另一种聊天室是
拉聊天室
大多数基于http的聊天室都是基于这种模式变形。
好处:支持无连接 支持无缝断线重连
坏处:具有公共数据 可能会引发并发冲突 可能会因为锁而降低性能。
参考下神奇的asp3年代我们怎么写聊天室:
2 <%
3 Response.Buffer=true ' 设 置 输 出 缓 存,用 于 显 示 不 同 页 面。
4 On error resume next ' 忽 略 程 序 出 错 部 分
5 If Request.ServerVariables("Request_Method")="GET" then
6 ' 判 断 客 户 是 以 什 么 方 式 请 求 WEB 页 面
7 '————————
8 ' 客 户 登 陆 界 面
9 '————————
10 %>
11 <form method="POST" action="http://www.cs02.com/luo40.asp">
12 <p>
13 <input type="text" name="nick" size="20" value="nick" style="background-color: rgb(192,192,192)"><br>
14 <input type="submit" value=" 进 入 聊 天 室 " name="B1" style="color: rgb(255,255,0);
15 font-size: 9pt; background-color: rgb(0,128,128)">
16 <p>
17 <input type="hidden" name="log" size="20" value="1"><br>
18 </p>
19 </form>
20 <%
21 Response.End ' 结 束 程 序 的 处 理
22 Else
23 Response.clear ' 清 空 缓 存 中 的 内 容
24 dim talk
25 If Request.Form("nick")<>"" then
26 ' 判 断 客 户 是 是 否 在 聊 天 界 面 中
27 Session("nick")=Request.Form("nick")
28 End If
29 '————————
30 '客 户 聊 天 界 面
31 '————————
32 %>
33 <form method="POST" action="http://www.cs02.com/luo40.asp" name="form1">
34 <p>
35 <%=Session("nick")%>
36 说 话:<input type="text" name="talk" size="50"><br>
37 <input type="submit" value=" 提 交 " name="B1">
38 <input type="reset" value=" 取 消 " name="B2"></p>
39 </form>
40 <a href="http://www.cs02.com/luo40.asp">离 开 </a>
41 <br>
42 <br>
43 <%
44 If Request.Form("log")<>1 then
45 If trim(Request.Form("talk"))="" then
46 ' 判 断 用 户 是 否 没 有 输 入 任 何 内 容
47 talk=Session("nick")&" 沉 默 是 金。"
48 Else
49 talk=trim(Request.Form("talk"))
50 ' 去 掉 字 符 后 的 空 格
51 End If
52
53 Application.lock
54 Application("show5")=Application("show4")
55 Application("show4")=Application("show3")
56 Application("show3")=Application("show2")
57 Application("show2")=Application("show")
58 Application("show")="<table border='0' cellpadding='0' cellspacing='0' width='85%'><tr><td width='100%' bgcolor='#C0C0C0'></td></tr><tr><td width='100%'><font color='#0000FF'> 来 自 "&Request.ServerVariables("remote_addr")&" 的 "&Session("nick")&time&" 说:</font>"&talk&"</td></tr><tr><td width='100%' bgcolor='#C0C0C0'></td></tr></table><br>"
59 Application.UnLock
60 Response.Write Application("show5")
61 Response.Write Application("show4")
62 Response.Write Application("show3")
63 Response.Write Application("show2")
64 Response.Write Application("show")
65 End If
66 End If
67 %>
68
我们看到了锁、原始社会的数据结构,暴力的队列实现,代码云,宗教,神秘主义与性。(女王怀孕了!谁干的?)
.net实现的看上去似乎很安全的代码
- class ChatRoom
- {
- System.Collections.Queue MessageQ=new System.Collections.Queue() ;
- void Say(Object message)
- {
- MessageQ.Enqueue(message);
- }
- IEnumerable ListenBy(Object user)
- {
- lock (MessageQ.SyncRoot )
- {
- foreach (var o in MessageQ)
- {
- if (o.toUser == user | o.date > DateTime.Now.AddMinutes (-1) )
- yield return o;
- }
- }
- }
- \
却暗含杀机-〉在 enqueue的时候如果queue 空间不足, 自扩张的过程是会影响枚举的
如何高性能实现"拉聊天室"而尽量做到无锁高并发?这就是下篇要讨论的。
“我拿什么来实现你 我的接口?”
- namespace WayneGameSolution.Chat
- {
- using System;
- using System.Collections.Generic;
- using WayneGameSolution.Membership;
- using System.ServiceModel;
- [ServiceContract]
- public interface IChatChannel
- {
- /// <summary>
- /// 清所有信息
- /// </summary>
- void Clear();
- /// <summary>
- /// 清所有\u-28729 ?期信息
- /// </summary>
- void ClearTimeouted();
- /// <summary>
- /// 得到用户所有未\u-29701 ?信息
- /// </summary>
- /// <param name="user">查信息的用户</param>
- /// <returns>所有未\u-29701 ?的信息</returns>
- ///
- [OperationContract]
- IEnumerable<IChatMessage> GetUserMessage(string user);
- /// <summary>
- /// 收到信息
- /// </summary>
- /// <param name="message">发\u-28671 ?的消息</param>
- ///
- [OperationContract]
- void ReceiveMessage(IChatMessage message);
- /// <summary>
- /// \u-26479 ?\u-28589 ?ID
- /// </summary>
- ///
- string ID { get; }
- IDictionary<string,DateTime > LastReceivedTime { get; }
- LinkedList<IChatMessage> MessageLinkList { get; }
- string Name { get; }
- int TimeoutSecond { get; set; }
- ChannelType Type { get; }
- }
- public enum ChannelType
- {
- PublicChannel,
- AreaChannel,
- GuildChannel,
- TemporyChannel,
- TeamChannel
- }
- }
- namespace WayneGameSolution.Chat
- {
- using System;
- using System.Collections.Generic;
- using WayneGameSolution.Membership;
- using System.ServiceModel;
- [ServiceContract]
- public interface IChatChannel
- {
- /// <summary>
- /// 清所有信息
- /// </summary>
- void Clear();
- /// <summary>
- /// 清所有\u-28729 ?期信息
- /// </summary>
- void ClearTimeouted();
- /// <summary>
- /// 得到用户所有未\u-29701 ?信息
- /// </summary>
- /// <param name="user">查信息的用户</param>
- /// <returns>所有未\u-29701 ?的信息</returns>
- ///