ASP网页在线聊天系统
用ASP做在线聊天,页线最头疼的聊天不是前端怎么画,而是系统怎么让服务器主动把新消息推给浏览器。HTTP协议是页线请求-应答模式,浏览器不开口,聊天服务器没法硬塞数据过去。系统2000年前后那批用ASP写聊天室的页线人,基本都绕不开一个土办法:定时刷新。聊天
我见过一个经典实现,系统核心就三个文件。页线default.asp负责渲染框架,聊天左边是系统用户列表,右边是页线消息区。真正干活的聊天是getmsg.asp和sendmsg.asp。getmsg.asp每次被调用,系统就从Application对象里读出最近N条消息,拼成HTML返回。前端用iframe或者XMLHTTP每隔两三秒请求一次这个页面。听起来笨,但确实能跑。
<% ' getmsg.asp 片段 Dim lastId, msgArr, i lastId = Request.QueryString("lastid") msgArr = Application("chat_msgs") If IsArray(msgArr) Then For i = 0 To UBound(msgArr, 2) If msgArr(0, i) > lastId Then Response.Write "<div>" & msgArr(1, i) & "</div>" End If Next End If %> Application对象是全局的,所有用户共享。存消息用二维数组,第一维是消息ID,第二维是内容。每次有人发消息,sendmsg.asp就把新内容追加进去,同时更新一个Application("last_id")。这里有个坑:多个用户同时写Application,必须用Application.Lock和Unlock包起来,否则数组会乱。我调过一段代码,没加锁,结果两条消息的内容串到了一起,用户看到的是“你好今天天气不错”这种缝合怪。
用户列表怎么维护?用Session。每个用户进来时分配一个SessionID,往Application("users")里塞一条记录。麻烦的是用户直接关浏览器,Session不会立刻过期。得靠一个心跳机制:前端每隔30秒调一次alive.asp,更新该用户的时间戳。后台再开个定时清理,把超过两分钟没心跳的踢掉。这个逻辑用ASP写有点别扭,因为ASP没有常驻定时器。常见做法是在global.asa的Session_OnStart里启动一个不靠谱的计时,或者干脆在每次有人访问时顺手清理一遍。
消息存储别用数据库。Access数据库在并发写入时锁表严重,十几个人同时聊天就能把页面卡死。用Application数组,内存操作快得多。但数组不能无限增长,得设个上限,比如保留最近200条。超出就把最老的挤掉。写这段代码时要注意边界,UBound和LBound算清楚,不然容易越界报错。
前端体验方面,iframe方案比XMLHTTP更简单,但每次刷新整个页面会闪。后来有人用隐藏iframe提交表单,只让返回的JS去更新父窗口的div,视觉上就不闪了。代码大概长这样:
<iframe name="hidden_frame" style="display:none"></iframe> <form target="hidden_frame" action="sendmsg.asp" method="post"> <input name="msg"> </form> sendmsg.asp处理完后返回一段脚本:<script>parent.document.getElementById('msgbox').innerHTML += '...';</script>。这就绕开了页面整体刷新。
安全性基本靠过滤。有人输入<script>标签,直接原样存进去,所有聊天的人都会中招。得把<和>转成实体。还有更狠的,用Server.Execute或者eval注入ASP代码。所以用户输入的内容,在写入Application之前,必须过一遍Replace,把危险字符干掉。
这套方案放到今天肯定被嫌弃。轮询浪费带宽,Application数组撑不住高并发,Session管理也粗糙。但在IIS 5.0和ASP 3.0的环境下,它确实能跑起来,而且代码量小,一个下午就能搭出原型。我见过一个小型内部系统,用这套东西撑了三年,最多同时在线四十来人,没出过大问题。前提是别聊太猛,消息一多,数组遍历就慢,前端刷新间隔得从两秒调到五秒。
真要改进,可以把轮询换成Comet,用Response.Buffer和死循环挂住连接。但ASP对长连接支持很差,IIS的线程池很快会被占满。所以那个年代的人,大多还是老老实实轮询。技术选择受限于环境,能跑、够用,比什么都实在。