当前位置:首页 > 教程 > Web前端 > 网页版在线聊天室

网页版在线聊天室

引言

实现网页版的在线聊天室的方法有很多,在没有来到HTML5之前,常见的有:定时轮询、长连接+长轮询、基于第三方插件(如FLASH的Socket),而如果是HTML5,则比较简单,可以直接使用WebSocket,当然HTML5目前在PC端并没有被所有浏览器支持,所以我的这个聊天室仍是基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室,这个聊天室其实是我上周周末完成的,功能简单,可能有些不足,但可以满足在线即时聊天需求,分享也是给大家提供一个思路,大家可以基于此来实现更好的在线即时聊天工具。

聊天

聊天室功能简介:

  • 支持多人进入同一个聊天室聊天;
  • 进入即离线均会自动生成通知信息显示在聊天室中,这样聊天的人们就知道谁进来了谁离开了;
  • 实时显示在线人员表列;
  • 无需数据库支持,全部存在内存中,当然有条件的可以采用分布式缓存或加一个数据库来存,这里演示就是用内存来存了。

下面就开始分享我的代码,由于采用原生的JS及AJAX,所以简单易懂,代码分别WEB前端及服务端(有点废话了)

WEB前端源代码如下:(ChatPage.html)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title></title>
    <style type="text/css">
        html, body {
            margin: 0px;
            padding: 0px;
            width: 100%;
            height: 100%;
            background-color: #f8f7f7;
            font-family: arial,sans-serif;
        }
 
        #layouttable {
            margin:0px;
            padding:0px;
            width:100%;
            height:100%;
            border:2px solid green;
            border-collapse:collapse;
            min-width:800px;
        }
 
            #layouttable td {
                border: 1px solid green;
            }
 
        .h100p {
            height:100%;
        }
 
        .midtr{height:auto;}
            .midtr tr td {
                height: 100%;
            }
 
        #chatmsgbox, #chatonlinebox {
            background-color:white;
            overflow-x: hidden;
            overflow-y: auto;
            overflow-wrap: break-word;
            height: 100%;
        }
 
        #chatonlinebox {
            background-color:#f5d0a8;
        }
 
        .rc, .sd {
            overflow:hidden;
        }
 
         .rc p {
            float: left;
            color: green;
        }
            .sd p {
                float: right;
                color: orange;
            }
    </style>
 
</head>
<body>
    <table id="layouttable">
        <colgroup>
            <col style="width:auto" />
            <col style="width: 200px;" />
        </colgroup>
        <tr style="height:30px; background-color:lightblue;color:yellow;">
            <td>
                欢迎进入梦在旅途的网页即时在线大众聊天室 - www.zuowenjun.cn:
            </td>
            <td>
                当前在线人员
            </td>
        </tr>
        <tr style="height:auto;" id="midtr">
            <td>
                <div id="chatmsgbox">
                </div>
            </td>
            <td>
                <div id="chatonlinebox">
                    <ul id="chatnames"></ul>
                </div>
            </td>
        </tr>
        <tr style="height:50px;">
            <td colspan="2">
                <label for="name">聊天妮称:</label>
                <input type="text" id="name" style="width:80px;" />
                <input type="button" id="btnsavename" value="确认进入" />
                <label for="msg">输入内容:</label>
                <input type="text" id="msg" style="width:400px;" />
                <input type="button" id="btnSend" value="发送消息" disabled="disabled" />
            </td>
        </tr>
    </table>
    <script type="text/javascript">
        var chatName = null;
        var oChatmsgbox, oMsg, oChatnames;
        var ajaxforSend, ajaxforRecv;
 
        //页面加载初始化
        window.onload = function () {
            document.getElementById("btnsavename").onclick = function () {
                this.disabled = true;
                var oName = document.getElementById("name");
                oName.readOnly = true;
                document.getElementById("btnSend").disabled = false;
                //receiveMsg();
                setChatStatus(oName.value,"on");
            }
 
            document.getElementById("btnSend").onclick = function () {
                sendMsg(oMsg.value);
            };
 
            //init
            oChatmsgbox = document.getElementById("chatmsgbox");
            oMsg = document.getElementById("msg");
            oChatnames = document.getElementById("chatnames");
            ajaxforSend = getAjaxObject();
            ajaxforRecv = getAjaxObject();
        }
 
        //离开时提醒
        window.onbeforeunload = function () {
            event.returnValue = "您确定要退出聊天室吗?";
        }
 
        //关闭时离线
        window.onunload = function () {
            setChatStatus(chatName, "off");
        }
 
        //设置聊天状态:在线 OR 离线
        function setChatStatus(name, status) {
            callAjax(getAjaxObject(), "action=" + status + "&name=" + name, function (rs) {
                if (!rs.success) {
                    alert(rs.info);
                    return;
                }
                if (status == "on") {
                    chatName = document.getElementById("name").value;
                    setTimeout("receiveMsg()",500);
                }
                loadOnlineChatNames();
            });
        }
 
        //加载在线人员名称列表
        function loadOnlineChatNames(){
            callAjax(getAjaxObject(), "action=onlines", function (rs) {
                var lis = "";
                for(var i=0;i<rs.length;i++)
                {
                    lis += "<li>"+ rs[i] +"</li>";
                }
                oChatnames.innerHTML = lis;
            });
        }
 
        //接收消息列表
        function receiveMsg() {
            callAjax(ajaxforRecv, "action=receive&name=" + chatName, function (rs) {
                if (rs.success) {
                    showChatMsgs(rs.msgs, "rc");
                }
                setTimeout("receiveMsg()", 500);
            });
        }
        //发送消息
        function sendMsg(msg) {
            callAjax(ajaxforSend, "action=send&name=" + chatName + "&msg=" + escape(msg), function (rs) {
                if (rs.success) {
                    showChatMsgs(rs.msgs, "sd");
                    oMsg.value = null;
                    //alert("发送成功!");
                }
            });
        }
 
        //显示消息
        function showChatMsgs(msgs, cssClass) {
            var loadonline = false;
            for (var i = 0; i < msgs.length; i++) {
                var msg = msgs[i];
                oChatmsgbox.innerHTML += "<div class='" + cssClass + "'><p>[" + msg.name + "] - " + msg.sendtime + " 说:<br/>" + msg.content + "</p></div>";
                if (msg.type == "on" || msg.type == "off")
                {
                    loadonline = true;
                }
            }
            if (loadonline)
            {
                loadOnlineChatNames();
            }
        }
 
        //调用AJAX
        function callAjax(ajax, param, callback) {
 
            ajax.open("post", "ChatHandler.ashx", true);
            ajax.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
            ajax.onreadystatechange = function () {
                if (ajax.readyState == 4 && ajax.status == 200) {
                    var json = eval("(" + ajax.responseText + ")");
                    callback(json);
                }
            };
            ajax.send(param);
        }
 
        //获取AJAX对象(XMLHttpRequest)
        function getAjaxObject() {
            var xmlhttp;
            if (window.XMLHttpRequest) {// code for IE7+, Firefox, Chrome, Opera, Safari
                xmlhttp = new XMLHttpRequest();
            }
            else {// code for IE6, IE5
                xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
            }
            return xmlhttp;
        }
 
    </script>
</body>
</html>

代码很简单,并都有注释,在此就不作说明了,如果有疑问欢迎在下方评论。

服务端(ChatHandler.ashx) 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<%@ WebHandler Language="C#" Class="ChatHandler" %>
 
using System;
using System.Web;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Web.Script.Serialization;
using System.Threading;
using System.Collections.Concurrent;
 
public class ChatHandler : IHttpHandler
{
 
    private class Msg
    {
        public string name { get; set; }
        public string sendtime { get; set; }
        public string content { get; set; }
        public string readednams { get; set; }
        public int readedCount { get; set; }
        public string type { get; set; }
    }
 
    private static List<Msg> msgs = new List<Msg>();
    private static ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim();
    private static object syncObject = new object(),syncObject1 = new object();
    private static List<string> onLineNames = new List<string>();
 
    public void ProcessRequest(HttpContext context)
    {
        string chatName = context.Request.Form["name"];
        string msg = context.Request.Form["msg"];
        string actionName = context.Request.Form["action"];
        JavaScriptSerializer jsSerializer = new JavaScriptSerializer();
 
        object responseObject = null;
 
        switch (actionName)
        {
            case "receive":
                {
                    responseObject = GetNewMessages(chatName);
                    break;
                }
            case "send":
                {
                    responseObject = SendMessage(chatName, msg, "normal");
                    break;
                }
            case "on":
            case "off":
                {
                    responseObject = SetChatStatus(chatName, actionName);
                    break;
                }
            case "onlines":
                {
                    responseObject = onLineNames;
                    break;
                }
        }
 
        context.Response.ContentType = "text/json";
        context.Response.Write(jsSerializer.Serialize(responseObject));
 
    }
 
    private object SetChatStatus(string chatName, string status)
    {
        if (status == "on")
        {
            if (onLineNames.Exists(s => s == chatName))
            {
                return new { success = false, info = "该聊天妮称已经存在,请更换一个名称吧!" };
            }
            lock (syncObject1)
            {
                onLineNames.Add(chatName);
            }
            SendMessage(chatName, "大家好,我进入聊天室了!", status);
            return new { success = true, info = string.Empty };
        }
        else
        {
            lock (syncObject1)
            {
                onLineNames.Remove(chatName);
            }
            SendMessage(chatName, "再见,我离开聊天室了!", status);
            return new { success = true, info = string.Empty };
        }
    }
 
    /// <summary>
    /// 获取未读的新消息
    /// </summary>
    /// <param name="chatName"></param>
    /// <returns></returns>
    private object GetNewMessages(string chatName)
    {
        //第一种:循环处理
        while (true)
        {
 
            var newMsgs = msgs.Where(m => m.name != chatName && !(m.readednams ?? "").Contains(chatName)).OrderBy(m => m.sendtime).ToList();
            if (newMsgs != null && newMsgs.Count() > 0)
            {
                lock (syncObject)
                {
                    newMsgs.ForEach((m) =>
                    {
                        m.readednams += chatName + ",";
                        m.readedCount++;
                    });
                    int chatNameCount = onLineNames.Count();
                    msgs.RemoveAll(m => m.readedCount >= chatNameCount);
                }
 
                return new { success = true, msgs = newMsgs };
            }
 
            Thread.Sleep(1000);
        }
 
 
        //第二种方法,采用自旋锁
        //List<Msg> newMsgs = null;
        //SpinWait.SpinUntil(() =>
        //{
        //    newMsgs = msgs.Where(m => m.name != chatName && !(m.readednams ?? "").Contains(chatName)).OrderBy(m => m.sendtime).ToList();
        //    return newMsgs.Count() > 0;
        //}, -1);
 
        //rwLock.EnterWriteLock();
        //newMsgs.ForEach(m =>
        //{
        //    m.readednams += chatName + ",";
        //    m.readedCount++;
        //});
        //rwLock.ExitWriteLock();
        //return new { success = true, msgs = newMsgs };
    }
 
    /// <summary>
    ///
    /// </summary>
    /// <param name="chatName"></param>
    /// <param name="msg"></param>
    /// <returns></returns>
    private object SendMessage(string chatName, string msg, string type)
    {
        var newMsg = new Msg() { name = chatName, sendtime = DateTime.Now.ToString("yyyy/MM/dd HH:mm"), content =HttpContext.Current.Server.HtmlEncode(msg), readednams = null, type = type };
        //rwLock.EnterWriteLock();
        lock (syncObject)
        {
            msgs.Add(newMsg);
        }
        //rwLock.ExitWriteLock();
        return new { success = true, msgs = new[] { newMsg } };
    }
 
 
 
    public bool IsReusable
    {
        get
        {
            return false;
        }
    }
 
}

代码也相对简单,实现原理主要是:

  • 聊天消息:循环获取未读的消息,在取出读的消息同时,将其标识为已读,全部已读的消息则删除;–我这里采用了两种方法,第二种方法被注释掉了,大家可以取消注释试试,也是不错的,比第一种更直观,建议使用;
  • 发送消息:实例化一个消息实例并加入到聊天消息集合中;
  • 状态切换:上线则加入到在线人员集合中,并生成一条上线消息放入到聊天消息集合中,离线则从在线人员集合中移除该人员信息,并生成一条离线消息放入聊天消息集合中;

注意事项,由于采用了全局静态集合,所以线程同步比较重要。

最终的实现效果展示如下:

网页聊天

如果觉得不错的话,希望给我一个关注吧。

  • << .Net Native 跨平台初体验
  • js: this,call,apply,bind小总结 >>
  • 作者:
    除非注明,本文原创:知道91,欢迎转载!转载请以链接形式注明本文地址,谢谢。
    原文链接:http://www.zhidao91.com/web-onlinechat/

    相关文章 近期热评 最新文章

    • HTML+CSS+JS 实现图标旋转效果
      本文讲解了使用HTML+CSS+JS 实现图标旋转效果。
    • js怎样获取和设置元素的属性
      本文讲解了js获取和设置元素的属性的方法,有需要的朋友可以看看
    • Node.Js 不一般的JavaScript
      互联网的火热使得JavaScript风光无限,且服务端的JavaScript也并不是什么新技术了,相关的框架也有不少,只是node.js的成功让他爆发式的出现在我们的视线中,让很多前端...
    • 前端程序员必须知道的优化小知识
      前端开发人员的一些小知识,Web 前端性能优化是个大话题,是个值得运维人员持续跟踪的话题,是被很多网站无情忽视的技术。除了后台需要在性能上做优化外,其实前端的页面...
    • HTML5与CSS3的完美搭配
      TML5将会取代1999年制定的HTML 4.01、XHTML 1.0标准,以期能在互联网应用迅速发展的时候,使网络标准达到符合当代的网络需求,为桌面和移动平台带来无缝衔接的丰富内容。
    • 前端程序员必须知道的chrome开发者技巧
      在Web开发者中,Google Chrome是使用最广泛的浏览器。六周一次的发布周期和一套强大的不断扩大开发功能,使其成为了web开发者必备的工具。你可能已经熟悉了它的部分功能...
    • 使用微信JDK实现微信接口签名验证
      要使用微信的接口必须在绑定的域名下测试;签名必须先向微信请求到access_token,然后用access_token再去请求jsapi_ticket,最后用jsapi_ticket和相关的参数按照ASCII码...
    • oracle数据库相关操作注意事项
      修改Oracle SGA(共享内存) 很多网站说修改Oracle的内存通过命令 如果你这么做了,那么恭喜你,你的Oracle数据库无法启动了。如果你已经这么做了,恢复Oracle启动的方...
    • oracle数据库相关操作注意事项
      修改Oracle SGA(共享内存) 很多网站说修改Oracle的内存通过命令 如果你这么做了,那么恭喜你,你的Oracle数据库无法启动了。如果你已经这么做了,恢复Oracle启动的方...
    • 使用微信JDK实现微信接口签名验证
      要使用微信的接口必须在绑定的域名下测试;签名必须先向微信请求到access_token,然后用access_token再去请求jsapi_ticket,最后用jsapi_ticket和相关的参数按照ASCII码...
    • ABP开发指南系列教程(2) – 多层架构...
      为了减少复杂性和提高代码的可重用性,采用分层架构是一种被广泛接受的技术。为了实现分层的体系结构,ABP遵循DDD(领域驱动设计)的原则,将工程分为四个层: 展现层(...
    • ABP开发指南系列教程(1) – 入...
      ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称。 ASP.NET Boilerplate是一个用最佳实践和流行技术开发现代WEB应用程序的新起点,它旨在成为一个通用的WE...
    • Windows下 JIRA + Agile + Mysql 破解...
      本文讲述了Windows下 JIRA + Agile + Mysql 破解安装示例教程
    • c#类的构造函数继承关系示例剖析
      本文通过示例代码讲解了c#子类的构造函数与父类的关系,子类怎样集成父类的构造函数的。