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

聊天室功能简介:

1。支持多人进入同一个聊天室聊天;

2。进入即离线均会自动生成通知信息显示在聊天室中,这样聊天的人们就知道谁进来了谁离开了;

3。实时显示在线人员表列;

4。无需数据库支持,全部存在内存中,当然有条件的可以采用分布式缓存或加一个数据库来存,这里演示就是用内存来存了。

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

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

<!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) 

<%@ 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;
}
} }

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

1。聊天消息:循环获取未读的消息,在取出读的消息同时,将其标识为已读,全部已读的消息则删除;--我这里采用了两种方法,第二种方法被注释掉了,大家可以取消注释试试,也是不错的,比第一种更直观,建议使用;

2。发送消息:实例化一个消息实例并加入到聊天消息集合中;

3。状态切换:上线则加入到在线人员集合中,并生成一条上线消息放入到聊天消息集合中,离线则从在线人员集合中移除该人员信息,并生成一条离线消息放入聊天消息集合中;

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

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

张三:

李四:

小美:

如果觉得不错的话,给个推荐吧,你的支持是推动我不断前进的动力及写作的源泉,我一直坚持:知识在于分享,分享的同时自己也在成长,希望与大家共同成长,谢谢!

分享一个基于长连接+长轮询+原生的JS及AJAX实现的多人在线即时交流聊天室的更多相关文章

  1. 短连接、长连接、轮询、长轮询、WebSocket

    短连接 建立连接——数据传输——关闭连接...建立连接——数据传输——关闭连接 定义:短连接是指通讯双方有数据交互时,就建立一个连接,数据发送完成后,则断开此连接,即每次连接只完成一项业务的发送. 应 ...

  2. 福利到~分享一个基于jquery的智能提示控件intellSeach.js

    一.需求 我们经常会遇到[站内搜索]的需求,为了提高用户体验,我们希望能做到像百度那样的即时智能提示.例如:某公司人事管理系统,想搜索李XX,只要输入“李”,系统自然会提示一些姓李的员工,这样方便用户 ...

  3. [UWP]分享一个基于HSV色轮的调色板应用

    1. 前言 上一篇文章介绍了HSV色轮,这次分享一个基于HSV色轮的调色板应用,应用地址:ColorfulBox - Microsoft Store 2. 功能 ColorfulBox是Adobe 色 ...

  4. 基于mykernel完成时间片轮询多道进程的简单内核

    基于mykernel完成时间片轮询多道进程的简单内核 原创作品转载请注明出处+中科大孟宁老师的linux操作系统分析:https://github.com/mengning/linuxkernel/ ...

  5. 分享一个基于 ABP(.NET 5.0) + vue-element-admin 管理后台

    1.前言 分享一个基于ABP(.NET 5.0) + vue-element-admin项目.希望可以降低新手对于ABP框架的学习成本,感兴趣的同学可以下载项目启动运行一下.对于想选型采用ABP框架的 ...

  6. 黑科技!仅需 3 行代码,就能将 Gitter 集成到个人网站中,实现一个 IM 即时通讯聊天室功能?

    欢迎关注个人微信公众号: 小哈学Java, 文末分享阿里 P8 高级架构师吐血总结的 <Java 核心知识整理&面试.pdf>资源链接!! 个人网站: https://www.ex ...

  7. 分享一个基于thrift的java-rpc框架

    简单介绍 这是一个简单小巧的Java RPC框架,适用于Java平台内.为系统之间的交互提供了.高性能.低延迟的方案.适合在集群数量偏少的情况下使用(50台以下集群环境).当然.它也可以在大型集群环境 ...

  8. 分享一个基于 netty 的 java 开源项目

    1.简介 中微子代理(neutrino-proxy)是一个基于 netty 的.开源的 java 内网穿透项目.遵循 MIT 许可,因此您可以对它进行复制.修改.传播并用于任何个人或商业行为. 2.项 ...

  9. 分享一个基于ligerui的系统应用案例ligerRM V2(权限管理系统)(提供下载)

    阅读目录 简介 系统特色 系统介绍 - 首页 系统介绍 - 列表页 系统介绍 - 明细页(表单) 系统介绍 - 菜单/按钮 系统介绍 - 权限中心 系统介绍 - 数据权限 系统介绍 - 字段权限 系统 ...

随机推荐

  1. iOS开发点滴:iPhone屏幕适配

    最近开始做iOS开发,遇到一些小问题和解决方法,记录下.   今天是iPhone屏幕适配 iPhone5出来之后屏幕就有iPhone就有了2种尺寸:3.5寸和4寸,xcode 5 的IB设计器里面界面 ...

  2. 那点你不知道的XHtml(Xml+Html)语法知识(DTD、XSD)

    什么是XHtml: 摘录网上的一句话,XHTML就是一个扮演着类似HTML的角色的XML. XHtml可当模板引擎应用: CYQ.Data 框架里有一套XHtmlAction模板引擎, 应用在QBlo ...

  3. SQL Server 复制订阅

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/高性能解决方案/高可用 概述 配置复制就没有数据库镜像和AlwaysOn的要求那么高,只需要两台服务器能通过TCP进行通讯即可,两 ...

  4. SQL Server 备份迁移策略

    标签:SQL SERVER/MSSQL SERVER/数据库/DBA/xp_cmdshell/备份压缩 概述 当备份空间不是很充裕的情况下需要找方法将备份文件拷贝到专用的备份机器上去,特别是存储空间不 ...

  5. 开源一个练手小App, PrintableCheckList

    A small but powerful App, only focus on one thing, make you easy to print out your checklist. It is ...

  6. Could not load type 'System.Reflection.AssemblySignatureKeyAttribute' from assembly 'mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c

    错误: Could not load type 'System.Reflection.AssemblySignatureKeyAttribute' from assembly 'mscorlib, V ...

  7. C语言 · 回文数

    问题描述 1221是一个非常特殊的数,它从左边读和从右边读是一样的,编程求所有这样的四位十进制数. 输出格式 按从小到大的顺序输出满足条件的四位十进制数.   方案一: int main(){ int ...

  8. Qt And MFC UI Layout

    界面布局 起初,计算机的交互是通过输入的代码进行的, 慢慢的有了图形之后, 就开始了图形界面的交互. 目前来说还有语音交互, 视频交互等多媒体的交互. 不管哪一种交互, 最终在计算机的角度都是信号的输 ...

  9. Report processing of Microsoft Dynamic AX

    Report processing of Microsoft Dynamic AX 版权声明:本文为博主原创文章,未经博主允许不得转载. The implementation of a general ...

  10. PHP从PHP5.0到PHP7.1的性能全评测

    本文是最初是来自国外的这篇:PHP Performance Evolution 2016, 感谢高可用架构公众号翻译成了中文版, 此处是转载的高可用架构翻译后的文章从PHP 5到PHP 7性能全评测( ...