当前位置: 首页 > 编程日记 > 正文

web聊天室总结

前言: 最近在写一个聊天室的项目,前端写了挺多的JS(function),导致有点懵比,出了BUG,也迟迟找不到。所以昨天把写过的代码总结了一下,写成博客。

项目背景

参考博客: http://www.cnblogs.com/alex3714/articles/5337630.html

 

先直观来几张图感受下

最开始的界面布局:

加点bootstrap样式:

 实时的聊天效果:

第一步:点击左侧界面的好友,触发事件,打开聊天界面

1.1、给点击好友添加active属性,使其高亮。

Alex Li是一个li标签,属性有联系类型,与Alex Li的用户id.

<li contact-type="single" id="1" class="list-group-item active" onclick="OpenChatWindow(this)"> </li>

1.2、上面的single与id是怎么来的呢? 见如下html代码。

    <ul class="list-group">{% for friend in request.user.userprofile.friends.select_related %}<li contact-type="single" id="{{friend.id}}" class="list-group-item"onclick="OpenChatWindow(this)"><span class="badge hide">14</span>  <!-- 新消息提醒数量 --><span class="contact-name">{{friend.name}}</span></li>{%endfor%}
</ul>

第二步:使界面标题框出现"正在和Alex Li聊天"字样。

并给其div 添加contact-id,与contact-type属性。

<div class="chat-box-title" contact-id="1" contact-type="single"><span>正在和 Alex Li 聊天</span></div>

第三步:通过事件委托(可看博客《聊一聊JQ中delegate事件委托的好处》)绑定事件,一按回车键就调用SendMsg(msg_text);发送消息

            // 事件委托$("body").delegate("textarea", "keydown", function (e) {  // e==eventif(e.which == 13){  // 按下键的数字(e.which);13是enter键的ASCII码var msg_text = $("textarea").val();if ($.trim(msg_text).length > 0){console.log(msg_text);// 发送消息给对方
                        SendMsg(msg_text);// 将发送的信息打印到自己的window界面
                        AddSendMsgIntoWindow(msg_text);$("textarea").val("");  // 将输入框清空}else {alert("请输入要发送的消息")}}});

第四步:通过ajax将消息发送到后台

4.1、消息的格式:

    var msg_item={"from":"{{request.user.userprofile.id}}","to":contact_id,"type":contact_type,"msg":msg_text  //要发送的消息
};

type为发送的格式:

  • 为single表示一对一发送;
  • 为group表示群发,此时的"to",后面的3表示群组id

前面都没什么难度的,到这里通过ajax将消息字典(json格式)发到后台,后台怎么处理么?

简单阿,将消息发给用户阿,那要是用户没登陆呢??那只好先将数据存起来,存在哪呢?要满足先进先出,可以用队列queue,当然,生产环境最好用rabbitmq(《python之rabbitMQ》);

4.2、后台每个用户都有一条队列queue. 后台的全局队列如下: 以用户的id作为key, 队列作为value

GLOBAL_MSG_QUEUES={"id": queue.Queue(),
}#队列:全局变量

4.3、消息字典存到待接收用户的队列中去,如果用户此时队列不存在,则先给待接收消息的用户生成对应的一条队列,再将消息字典存到待接收用户的队列中去。

1 #如果用户队列不存在,注意,如果id(key)对应的队列不存在,则输出None,不会曝错
2 if not GLOBAL_MSG_QUEUES.get(queue_id):
3     GLOBAL_MSG_QUEUES[queue_id]=queue.Queue()    #创建队列
4     
5 GLOBAL_MSG_QUEUES[queue_id].put(msg_dic)    #将消息字典(带时间戳)放进队列

4.4、将消息字典(含时间戳)成功存到待接收用户的队列后,ajax请求完毕,返回"---receive msg---",表示后台已成功接收到要发送的消息,表示消息字典成功存到待接收用户的队列中。

return HttpResponse("---receive msg---")

第五步:将发送的信息打印到自己的window界面,调用AddSendMsgIntoWindow(msg_text);

function AddSendMsgIntoWindow(msg_text){varnew_msg_ele="<divclass='msg-item'>"+"<span>" + "{{request.user.userprofile.name}}" + "</span>" +"<span>" + newDate().toLocaleDateString() + "</span>" +"<divclass='msg-text'>" + msg_text + "</div>" +"</div>";$(".chat-box-window").append(new_msg_ele);$(".chat-box-window").animate({scrollTop:$(".chat-box-window")[0].scrollHeight//每隔0.5s自动向下滚动},500);//console.log($(".chat-box-window")[0]);
}

将消息打印到自己的界面一开始会出现问题,比如你发了很多数据,界面整个div已经装不下了,就会“溢出”div。简单阿,给聊天界面加个"overflow"样式就行了

overflow: auto;  /* 给div 内容多了自动加滚动条 */

太棒了,现在聊天内容一多,就自动出现滚动条,牛!! 但问题又来了,虽然有滚动条,但每次你一发消息,都得自己去拉滚动条到最底部才能看到刚刚发的消息。这……

于是我上网找到了这篇博客:scrollTop 和 scrollHeight的意思

今天要用到实时显示最近更新内容,也就是要让对话框随时都在最底部。
查了一下,
用div.scrollTop=div.scrollHeight;就可以了。
又查了查这两个参数什么意思。stackoverflow上面有人是这样解答的。If I scroll down 5px in this window, the window's scrollTop value is 5. If I scroll right 10px in a scrollable div, the div's scrollLeft value is 10.
When I scroll to the top left corner of this window, both its scrollTop and scrollLeft values are 0.
还有一个人作了补充: scrollTop and scrollHeight. In summary, scrollTop is how much it's currently scrolled, and scrollHeight is the total height, including content scrolled out of view.
总的来说,scrollTop就是卷起来的部分,也就是我们随着下拉,看不见的部分。scrollHeight就是整个窗口可以滑动的高度

so, 我用下面的方法就可以解决了,每隔0.5s 自动向下滚动至底部, animate() 方法

$(".chat-box-window").animate({
scrollTop:$(".chat-box-window")[0].scrollHeight
},5000);

第六步:界面一加载完毕就开始调用GetNewMsgs();开始取消息,向后台发起ajax起求

如何取消息?? 即是说:A向B发数据,后台收到A的数据后,如何返回给B

  1. 后台设置一个队列,将A发送的消息存到专属于B的队列中。
  2. 前端写一个定时器,每隔3秒就通过ajax去后台查询用户的队列是否为空,不为空的话,则取出数据。
{#            用定时器浏览器会崩(卡)#}
{#            setInterval(function () {#}
{#                GetNewMsgs();#}
{#            }, 3000)#}

  3、用定时器的话,会出现消息不实时的情况。比如,A用户啪啪啪很快发了很多数据,但B用户每隔3秒才去后台取数据,中间有最多3秒的时延。这就出现消息不实时的问题。

  4、用户查询自己的队列中是否有无数据,无数据的话则后台挂起。

     # 超时挂起# 若队列为空,则60秒后会曝queue.Empty异常(相当在这60秒内卡住挂起)try:msg_list.append(q_obj.get(timeout=60))except queue.Empty:  # 若队列为空,则60秒后会曝queue.Empty异常(相当在这60秒内卡住挂起)print("\033[41;1mno msg for [%s][%s],timeout\033[0m" % (request.user.userprofile.id,request.user))

虽然队列无数据时,后台可以挂起,但是前端用定时器每3秒发起一次ajax起求,问 "我的队列中有没有来新数据阿??", 每次起求相当于浏览器起一个线程,时间一长,浏览器会卡,撑不住。这怎么办呢?? 难道不能用定时器?? 那还能用什么牛逼屌炸天的方法?

按我最好情况的理解是,前端每发起一次请问,若队列有数据,则立马取数据,若无数据,则挂起60秒(时间可在后台设置),这60秒内若队列中有数据,同样从队列取数据,若60秒内队列都无数据,则出queue.Empty异常,此时前端再发起ajax请求。

在解决上面的浏览器线程过多,太卡前,我们先来看看后台是如何处理队列为空时挂起 这一功能的。看下面这张图加上上面第4点的代码,你应该懂的。

好,回到浏览器太卡这个BUG上来,我用了递归这个方法,代码如下:

 1         // 用户获取消息
 2         function GetNewMsgs() {
 3             console.log("----getting new msg----");
 4             $.getJSON("{% url 'get_new_msgs' %}",
 5                 function (callback) {
 6                     // callback是列表对象,object, 列表每个元素都是一个消息字典
 7                     console.log(callback, typeof callback);  // Array [Object] object
 8                     // 解析消息,用户可能收到与当前正在聊天用户的消息,也可能是其他用户发的消息
 9                     ParseNewMsgs(callback);
10 
11                     return GetNewMsgs();  // 递归
12                 })
13         }

不用定时器。前端发起一个ajax请求,后端队列无数据则挂起。当后台向前端返回数据时,有两种情况,一种是超时,如60秒内,队列都无新消息,出queue.Empty异常后返回数据;第二种是用户接收到别人发给他的数据,队列一有数据,则返加给前端。前端收到数据后,回调函数再发起一个ajax请求。

若前端没收到数据(在后台挂起的时间内),则不会发起ajax请求,此时相当于实现前端挂起。不会起太多的线程。

(注: python专门设置的一种机制用来防止无限递归造成Python溢出崩溃。在python的递归是有层数据限制的,999层。超过就抛出 “RuntimeError: maximum recursion depth exceeded” )

第七步

后台查询用户队列中是否有消息(数据字典),无的话就挂起,一分钟无消息,则前端再发起ajax请求。若队列中有消息的话,则返回给用户,返回形式是列表形式,列表每个元素都是数据字典形式

eg:{"from":"3","to":"2","type":"single","msg":"1111"} <class 'str'>return HttpResponse(json.dumps(msg_list))#序列化,转化为json格式

第八步:

问题: A用户与B女神聊的正嗨,点击左侧好友切换到C女神, 与C聊天,但聊天窗口,依旧是与B女神聊天的内容。

接下来要完成视图切换功能,在切换之前将原本的视图的html元素存到全局字典

   // 全局字典,用于存切换视图前的html元素GLOBAL_CHAT_RECORD_DIC = {"single":{},"group":{}};

全局字典的作用可将C女神发来的消息存入,格式应在"single"的value(字典),再设置一个用户id与用户聊天数据的字典(C用户的id为3,xxx为将经过处理的html元素)。

如:"single":{"3": "xxx"}, 

切换视图前将与B女神聊天窗口的html元素(数据)添加到用户的全局字典中去:

// 视图切换,在切换之前将原本的视图的html元素存到全局字典
// 原本的框题框contact-id属性不为空,即切换视图前左侧已有点击对象
if ($(".chat-box-title").attr("contact-id")){// 从标题框取出切换前用户的id,与联系方式contact-typevar session_id = $(".chat-box-title").attr("contact-id");var session_type = $(".chat-box-title").attr("contact-type");// 将切换产前的视图存入全局字典GLOBAL_CHAT_RECORD_DIC[session_type][session_id] = $(".chat-box-window").html();
}

框题框显示"正在与C女神聊天"后,从全局字典取出与C的聊天数据,并显示在聊天窗口。

// 把chat-window的html元素从全局字典中存出来
// 第一次点击该联系人时,chat_record为undefined,因为字典的single下还没有生成id(key)对应的value
var chat_record = GLOBAL_CHAT_RECORD_DIC[contact_type][contact_id];
console.log(chat_record,typeof chat_record);
if (typeof chat_record == "undefined"){$(".chat-box-window").html("");
}else {// 如果chat_record为undefined,则下面代码无法将对话界面清空(重要)$(".chat-box-window").html(chat_record);console.log("haha>>", chat_record)
}

视图切换功能基本完成。

第九步:前端通过ajax接收到后台返回的数据后,将数据渲染成html样式后显示在聊天窗口。

这里就要分情况了。

如果用户收到的是与当前正在聊天用户的消息,直接将html元素添加到聊天窗口$(".chat-box-window").append(new_msg_ele);  否则,如:用户A正在与B聊天,此时收到来自C发来的消息。C发来的消息要发在哪里呢? 当然是前面设置的全局变量啊!!

    // 用户收到的是与当前正在聊天用户的消息if (current_session_id==callback[msg_item]["from"] && current_session_type==callback[msg_item]["type"]){// 将消息的html元素添加到聊天窗口$(".chat-box-window").append(new_msg_ele);}else {// 用户没打开消息发送者的对话框,消息暂存到内存中(全局变量中)if (typeof GLOBAL_CHAT_RECORD_DIC[callback[msg_item]["type"]][callback[msg_item]["from"]]=="undefined"){GLOBAL_CHAT_RECORD_DIC[callback[msg_item].type][callback[msg_item].from]=new_msg_ele;}else {  // 如果GLOBAL_CHAT_RECORD_DIC[current_session_type][current_session_id]不为undefinedGLOBAL_CHAT_RECORD_DIC[callback[msg_item].type][callback[msg_item].from]+=new_msg_ele;}}

先写这么多吧。转发注明出版: http://www.cnblogs.com/0zcl/p/6903017.html

前几天学了git,有想一起做这个小项目的么??一个人写代码总感觉太慢了,有想一起完成的可以看看我的git项目https://github.com/0zcl/bbs_project,可以pull给我。欢迎交流。

相关文章:

概率图论PGM的D-Separation(D分离)

为什么80%的码农都做不了架构师&#xff1f;>>> 本文大部分来自&#xff1a;http://www.zhujun.me/d-separation-separation-d.html 其中找了一些资料发现原文中阻塞&#xff08;block&#xff09;中&#xff08;b&#xff09;部分有出路&#xff0c;黑体部分修改…

CSDN湘苗培优|高起点步入职场,快人一步!

课程了解3个培养阶段结束后&#xff0c;让你具备&#xff1a;解决问题能力、交付能力、有经验。系统基础训练&#xff08;阶段一&#xff09;•内容&#xff1a;程序逻辑基础、计算机原理、操作系统工作原理、C语言&#xff08;掌握内存的分配&#xff09;、密码学、信息论、概…

php与Ajax实例

****************AJAX的学习要有JavaScript、HTML、CSS等基本的Web开发能力**************** [AJAX介绍] Ajax是使用客户端脚本与Web服务器交换数据的Web应用开发方法。Web页面不用打断交互流程进行重新加裁&#xff0c;就可以动态地更新。使用Ajax&#xff0c;用户可以创建…

[转]构建基于WCF Restful Service的服务

本文转自&#xff1a;http://www.cnblogs.com/scy251147/p/3566638.html 前言 传统的Asmx服务&#xff0c;由于遵循SOAP协议&#xff0c;所以返回内容以xml方式组织。并且客户端需要添加服务端引用才能使用&#xff08;虽然看到网络上已经提供了这方面的Dynamic Proxy&#xff…

Ajax使用初步

Ajax定义为“Asynchronous JavaScript XML”的简称&#xff0c;也就是异步的JavaScript和XML处理。从原理上看&#xff0c;主要是Ajax可以通过调用HttpRequest实现与服务器的异步通讯&#xff0c;并最终在网页中实现丰富友好的用户界面Ajax使用初步&#xff0c;配置步骤1.把Aj…

AI化身监工,上班还能摸鱼吗?

来源 | 人民数字FINTECH责编 | 晋兆雨头图 | CSDN 下载自视觉中国俗话说“上班摸鱼一时爽&#xff0c;一直摸鱼一直爽。”上班族这群“时间管理大师们”往往能在上班的时间中挤出一半的时间来摸鱼&#xff1a;在距离上班时间的最后一分钟打卡&#xff0c;午饭时间未到就打开各大…

解决“安装程序无法定位现有系统分区,也无法创建新的系统分区”的方法

使用老毛桃PE格式化C盘后安装Win7出现“安装程序无法定位现有系统分区,也无法创建新的系统分区”的错误。本文给出了我遇到该情况的解决办法&#xff0c;亲身经历&#xff0c;绝非抄袭。 在网上看了好多办法&#xff0c;都无效。最后竟然用下面的方法成功了&#xff1a; 1. 使用…

Linux 上 12 个高效的文本过滤命令

在这篇文章中&#xff0c;我们将会看一些 Linux 中的过滤器命令行工具。过滤器是一个程序&#xff0c;它从标准输入读取数据&#xff0c;在数据上执行操作&#xff0c;然后把结果写到标准输出。 因此&#xff0c;它可以用来以强大的方式处理信息&#xff0c;例如重新结构化输出…

linux在多核处理器上的负载均衡原理

原文出处&#xff1a;http://donghao.org/uii/ 【原理】 现在互联网公司使用的都是多CPU&#xff08;多核&#xff09;的服务器了&#xff0c;Linux操作系统会自动把任务分配到不同的处理器上&#xff0c;并尽可能的保持负载均衡。那Linux内核是怎么做到让各个CPU的压力均匀的呢…

完全免费,简化版Plotly推出,秒绘各类可视化图表

作者 | Peter来源 | Python编程时光今天给大家推荐一个可视化神器 - Plotly_express &#xff0c;上手非常的简单&#xff0c;基本所有的图都只要一行代码就能绘出一张非常酷炫的可视化图。以下是这个神器的详细使用方法&#xff0c;文中附含大量的 GIF 动图示例图。环境准备本…

Linux 启动过程详解

说明&#xff1a;由于图片太大&#xff0c;上传博客的图片是jpg格式的有点失真&#xff0c;看不清楚&#xff0c;可以双击打开查看&#xff0c;有朋友想看高清&#xff0c;无码&#xff0c;无水印的大图&#xff08;png格式&#xff09;请下载附件&#xff01;转载于:https://b…

java web项目优化记录:优化考试系统

考试系统在进行压力測试时发现&#xff0c;并发量高之后出现了button无反应。试题答案不能写到数据库的问题&#xff0c;于是针对这些核心问题&#xff0c;进行了优化。 数据库方面&#xff1a; Select语句&#xff1a;Select * from TEB_VB_XZTRecord改为select 必须的列 form…

深度学习中的注意力机制(三)

作者 | 蘑菇先生来源 | NewBeeNLP原创出品 深度学习Attenion小综述系列&#xff1a;深度学习中的注意力机制&#xff08;一&#xff09; 深度学习中的注意力机制&#xff08;二&#xff09;目前深度学习中热点之一就是注意力机制&#xff08;Attention Mechanisms&#xff…

程序分析工具gprof介绍

程序分析是以某种语言书写的程序为对象&#xff0c;对其内部的运作流程进行分析。程序分析的目的主要有三点&#xff1a;一是通过程序内部各个模块之间的调用关系&#xff0c;整体上把握程序的运行流程&#xff0c;从而更好地理解程序&#xff0c;从中汲取有价值的内容。二是以…

hadoop源码datanode序列图

2019独角兽企业重金招聘Python工程师标准>>> 转载于:https://my.oschina.net/u/572882/blog/134796

HDU 2206 IP的计算(字符串处理)

题目链接&#xff1a;http://acm.hdu.edu.cn/showproblem.php?pid2206 Problem Description在网络课程上&#xff0c;我学到了非常多有关IP的知识。IP全称叫网际协议&#xff0c;有时我们又用IP来指代我们的IP网络地址&#xff0c;如今IPV4下用一个32位无符号整数来表示&#…

有规律格式化文本文件插入数据库

现有以下文本文件: *理光(深圳)工业发展有限公司(D15)(位于福田区)1.厨师1名;男;30岁以下;高中以上学历;中式烹调师中级以上&#xff0c;需备齐身份证/毕业证/流动人口婚育证明原件及复印件1份.经公司体检不合格者将不予录用&#xff0c;不合格者体检费自理.福利及待遇:工作时…

java使用uploadify上传文件

一、简介Uploadify是JQuery的一个上传插件&#xff0c;实现的效果非常不错&#xff0c;带进度显示&#xff1b;可以上传多个文件&#xff1b;详细的使用方法网上有很多&#xff0c;建议到官网参考&#xff0c;这里仅仅展示其使用的效果&#xff1b;官网&#xff1a;www.uploadi…

微软亚洲研究院成立OpenNetLab,探索以“数据为中心”AI网络研究新范式!

2020年12月18日&#xff0c;微软亚洲研究院宣布联合清华大学、北京大学、南京大学、兰州大学、新加坡国立大学、首尔国立大学等多所亚洲地区高校&#xff0c;成立OpenNetLab开放网络平台联盟。 OpenNetLab官网地址&#xff1a;https://opennetlab.org/ 通过为研究人员提供通用的…

圆角文本框的制作

把border&#xff1a;0px;outline:none;就可以清除边框。然后在外面放一个圆角div&#xff0c;文本框在div内居中的话能够&#xff0c;设置行高和text-align:center。或者也能够在背景图上放文本框。

微软收购 GitHub 两年后,大咖共论开源新生态

头图 | CSDN 下载自视觉中国被微软收购两年的GitHub&#xff0c;现在怎么样了&#xff1f;据《 2020 年度 GitHub Octoverse 报告》显示&#xff0c;GitHub 上开发者数量达到 5600 万&#xff0c;新增 6000 万个存储库以及 19 亿个 contribution。GitHub 预计到 2025 年&#x…

网页中如何获取客户端系统已安装的所有字体?

如何获取系统字体&#xff1f;1.首先在需要获取系统字体的网页<body>后加入以下代码&#xff1a;<DIV style"LEFT: 0px; POSITION: absolute; TOP: 0px"><OBJECT ID"dlgHelper" CLASSID"clsid:3050f819-98b5-11cf-bb82-00aa00bdce0b&q…

Web开发常见的软件架构

Web开发常见的软件架构 一、看需求分析&#xff0c;看产品PRD&#xff1a;Product Requirement Document 二、根据PRD和产品原型建数据库表,注意三范式要求,用工具到处数据库关系图&#xff0c;并快速地理清数据库思路 三、搭建项目架构&#xff0c;常用三层&#xff0c;自动…

thinkphp整合系列之gulp实现前端自动化

这又是一个一次整合终身受益&#xff1b;不止是终身&#xff1b;换个项目同样可以很方便复用&#xff1b;不信你看另一个项目&#xff1a; thinkphp整合系列之gulp实现前端自动化 虽然我等叫php程序猿&#xff1b;但是不可避免的是要跟html打交道的&#xff1b;而且php这么容易…

网上几种常见校验码图片分析

前几天受刺激了&#xff0c;准备把CSDN的校验码图片修改。就上网找了一些参考示例。和分析了一些校验码的功能。不敢独享&#xff0c;整理到一起&#xff0c;跟大家分享。 至于CSDN新的校验码写法&#xff0c;不是这里面的任何一种。也不是网上可以找到的。这个不好公开&#…

语言都是相通的,学好一门语言,再学第二门语言就很简单,记录一下我复习c语言的过程。...

语言都是相通的&#xff0c;学好一门语言&#xff0c;再学第二门语言就很简单&#xff0c;记录一下我复习c语言的过程。为了将本人的python培训提高一个层次&#xff0c;本人最近买了很多算法的书.这个书上的代码基本都是c语言实现的&#xff0c;c语言很久没有用了&#xff0c;…

百度飞桨全新升级:重磅推出PaddleHelix平台、开源框架V2.0RC,硬件生态路线图全公开...

12月20日&#xff0c;WAVE SUMMIT2020深度学习开发者峰会在北京举办。本届峰会&#xff0c;百度飞桨带来八大全新发布与升级&#xff0c;有支持前沿技术探索和应用的生物计算平台PaddleHelix螺旋桨&#xff0c;开发更加便捷的飞桨开源框架2.0 RC版&#xff0c;端云协同的AI集成…

Java解压zip文件(文本)压缩包

2019独角兽企业重金招聘Python工程师标准>>> 说明&#xff1a;由于我们的日志收集到指定服务器上&#xff0c;会按天压缩成一个zip格式的压缩包&#xff0c;但是有时候需要对这些日志进行处理&#xff0c;人工解压在处理&#xff0c;显示对于大量的日志处理是不行的…

Asp.Net 动态生成验证码

我们在设计用户登录模块时&#xff0c;经常会用到验证码&#xff0c;可以有效地防止黑客软件的恶意破解&#xff0c;现公开我常用的验证码的源代码&#xff0c;生成效果如图&#xff1a;。使用方法&#xff1a;1、在Web项目中添加一个类&#xff0c;如“CreateImage.cs”&#…

Hyper-v 3.0虚拟化平台群集共享磁盘无法failover的故障

碰到一个hyper-v 3.0虚拟化平台和HP存储的兼容性问题&#xff0c;放出来和大家分享一下。平台&#xff1a;windows server 2012 RTMhyper-v 3.0故障现象&#xff1a;生产虚拟平台宿主机意外重启&#xff0c;且重启后一块存储磁盘变成脱机状态&#xff0c;进一步测试发现宿主机上…