WEB聊天

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WEB聊天相关的知识,希望对你有一定的参考价值。

功能需求:

  1. 用户可以与好友一对一聊天
  2. 可以搜索、添加某人为好友
  3. 用户可以搜索和添加群
  4. 每个群有管理员可以审批用户的加群请求,群管理员可以用多个,群管理员可以删除、添加、禁言群友
  5. 可以与聊天室里的人进行临时会话(与qq群一样)
  6. 可以在群中发图片
  7. 可以与好友一对一发文件

知识必备:

  1. django 
  2. html\\css\\js
  3. bootstrap
  4. jquery, ajax

 

前景介绍

首先我们知道http是无状态、请求/响应模式的通信模式,就是用户每次通过浏览器点击一下页面,都需要重新与WEB服务器建立一次连接,且发送自己的session id给服务器端以使服务器端验证此用户的身份。 客户端若想从web服务器上获取数据,必须主动发起一个请求,然后接收服务器端的返回,服务器端不会主动往客户端推送消息。 

基于传统的WEB服务器只会被动响应客户端请求的这个认知,如果我们想实现WEB实时聊天的需求,基本上只有以下几种方式:

轮询(polling)

轮询 (Polling) 涉及了从客户端向服务器端发出请求以获取一些数据,这显然就是一个纯粹的 Ajax HTTP 请求。为了尽快地获得服务器端事件,轮询的间隔(两次请求相隔的时间)必须尽可能地小。但有这样的一个缺点存在:如果间隔减小的话,客户端浏览器就会发出更多的请求,这些请求中的许多都不会返回任何有用的数据,而这将会白白地浪费掉带宽和处理资源。

技术分享

Comet

使用了轮询的Ajax 非常受限:其不具伸缩性,不提供低延迟通信(只要事件一到达服务器端,它们就以尽可能快的速度到达浏览器端)。 Comet 是一个 Web 应用模型,在该模型中,请求被发送到服务器端并保持一个很长的存活期,直到超时或是有服务器端事件发生。在该请求完成后,另一个长生存期的 Ajax 请求就被送去等待另一个服务器端事件。

Comet 的一大优点是,每个客户端始终都有一个向服务器端打开的通信链路。服务器端可以通过在事件到来时立即提交(完成)响应来把事件推给客户端,或者它甚至可以累积再连续发送。因为请求长时间保持打开的状态,故服务器端需要特别的功能来处理所有的这些长生存期请求。

 

Comet的实现主要有两种方式:

基于Ajax的长轮询(long-polling)方式

浏览器发出XMLHttpRequest 请求,服务器端接收到请求后,会阻塞请求直到有数据或者超时才返回,浏览器JS在处理请求返回信息(超时或有效数据)后再次发出请求,重新建立连接。在此期间服务器端可能已经有新的数据到达,服务器会选择把数据保存,直到重新建立连接,浏览器会把所有数据一次性取回。

Forever Iframe(永存的 Iframe)

此技术涉及了一个置于页面中的隐藏 Iframe 标签,该标签的 src 属性指向返回服务器端事件的 servlet 路径。每次在事件到达时,servlet 写入并刷新一个新的 script 标签,该标签内部带有 javascript 代码,iframe 的内容被附加上这一 script 标签,标签中的内容就会得到执行。

 

 技术分享

WebSocket 

如果说Ajax的出现是互联网发展的必然,那么Comet技术的出现则更多透露出一种无奈,仅仅作为一种hack技术,因为没有更好的解决方案。Comet解决的问题应该由谁来解决才是合理的呢?浏览器,html标准,还是http标准?主角应该是谁呢?本质上讲,这涉及到数据传输方式,http协议应首当其冲,是时候改变一下这个懒惰的协议的请求/响应模式了。

W3C给出了答案,在新一代html标准html5中提供了一种浏览器和服务器间进行全双工通讯的网络技术Websocket。从Websocket草案得知,Websocket是一个全新的、独立的协议,基于TCP协议,与http协议兼容、却不会融入http协议,仅仅作为html5的一部分。于是乎脚本又被赋予了另一种能力:发起websocket请求。这种方式我们应该很熟悉,因为Ajax就是这么做的,所不同的是,Ajax发起的是http请求而已。 

与http协议不同的请求/响应模式不同,Websocket在建立连接之前有一个Handshake(Opening Handshake)过程,在关闭连接前也有一个Handshake(Closing Handshake)过程,建立连接之后,双方即可双向通信。

 

技术分享

以上几种实现方式的优缺点

轮询:客户端定时向服务器发送Ajax请求,服务器接到请求后马上返回响应信息并关闭连接。 
优点:后端程序编写比较容易。 
缺点:请求中有大半是无用,浪费带宽和服务器资源。 
实例:适于小型应用。


长轮询:客户端向服务器发送Ajax请求,服务器接到请求后hold住连接,直到有新消息才返回响应信息并关闭连接,客户端处理完响应信息后再向服务器发送新的请求。 
优点:在无消息的情况下不会频繁的请求,耗费资源小。 
缺点:服务器hold连接会消耗资源,返回数据顺序无保证,难于管理维护。 
实例:WebQQ、Hi网页版、Facebook IM。

 

长连接:在页面里嵌入一个隐蔵iframe,将这个隐蔵iframe的src属性设为对一个长连接的请求或是采用xhr请求,服务器端就能源源不断地往客户端输入数据。 
优点:消息即时到达,不发无用请求;管理起来也相对方便。 
缺点:服务器维护一个长连接会增加开销。 
实例:Gmail聊天


Flash Socket:在页面中内嵌入一个使用了Socket类的 Flash 程序JavaScript通过调用此Flash程序提供的Socket接口与服务器端的Socket接口进行通信,JavaScript在收到服务器端传送的信息后控制页面的显示。 
优点:实现真正的即时通信,而不是伪即时。 
缺点:客户端必须安装Flash插件;非HTTP协议,无法自动穿越防火墙。 
实例:网络互动游戏。

 

WEBQQ操作

基本框架的搭建(基于BBS页面搭建):

1.通过项目的的url,路由到webqqApp

技术分享
 1 """s15BBS URL Configuration
 2 
 3 The `urlpatterns` list routes URLs to views. For more information please see:
 4     https://docs.djangoproject.com/en/1.10/topics/http/urls/
 5 Examples:
 6 Function views
 7     1. Add an import:  from my_app import views
 8     2. Add a URL to urlpatterns:  url(r‘^$‘, views.home, name=‘home‘)
 9 Class-based views
10     1. Add an import:  from other_app.views import Home
11     2. Add a URL to urlpatterns:  url(r‘^$‘, Home.as_view(), name=‘home‘)
12 Including another URLconf
13     1. Import the include() function: from django.conf.urls import url, include
14     2. Add a URL to urlpatterns:  url(r‘^blog/‘, include(‘blog.urls‘))
15 """
16 from django.conf.urls import url,include
17 from django.contrib import admin
18 
19 from bbs import views
20 
21 urlpatterns = [
22     url(r^admin/, admin.site.urls),
23     url(r^$, views.index ),
24     url(r^category/(\\d+|all)/$, views.category,name="category" ),
25     url(r^article/(\\d+)/$, views.article_detail,name="article_detail" ),
26     url(r^account/login/$, views.acc_auth,name="login" ),
27     url(r^account/logout/$, views.acc_logout,name="logout" ),
28     url(r^new_article/$, views.new_article,name="new_article" ),
29     url(r^webqq/,include(webqq.urls) ),
30 ]
BBSurl路由
技术分享
1 from django.conf.urls import url,include
2 from webqq import views
3 urlpatterns = [
4     url(rdashboard/$,views.dashboard,name=qq_dashboard)
5 ]
webqq_url
技术分享
 1 {% extends index.html %}
 2 {% block extra-head-resources %}
 3     <link href="/static/css/webqq_style.css" rel="stylesheet">
 4 {% endblock %}
 5 {% block container %}
 6     <div class="row" style="margin-top: 10px">
 7         <div class="col-lg-2 contact_list">
 8             <ul class="nav nav-tabs" role="tablist">
 9               <li role="presentation"><a href="#contacts" aria-controls="profile" role="tab" data-toggle="tab">C</a></li>
10               <li role="presentation"><a href="#groups" aria-controls="profile" role="tab" data-toggle="tab">G</a></li>
11             </ul>
12             <div class="tab-content">
13                 <div role="tabpanel" class="tab-pane active" id="contacts">contacts</div>
14                 <div role="tabpanel" class="tab-pane" id="groups">groups</div>
15             </div>
16 
17 </div>
18         </div>
19         <div class="col-lg-8 chat_panel">
20             <div class="row chat_panel_header">row chat_panel_header</div>
21             <div class="row chat_panel_body">row chat_panel_body</div>
22             <div class="row chat_panel_input">row chat_panel_input</div>
23         </div>
24     </div>
25 
26 {% endblock %}
dashboard.html

自定义html样式,需要在css或者js中导入

技术分享
 1 .contact_list{
 2     height: 600px;
 3     border: 1px dashed black;
 4 }
 5 .chat_panel{
 6     height:600px;
 7     border: 1px dashed red;
 8 }
 9 .chat_panel_header{
10     height: 50px;
11     border: 1px dashed orange;
12 }
13 .chat_panel_body{
14     height: 450px;
15     border: 1px dashed red;
16 }
17 .chat_panel_input{
18     height: 100px;
19     border: 1px dashed deeppink;
20 }
webqq_stule.css
技术分享
1 from django.shortcuts import render
2 
3 # Create your views here.
4 def dashboard(request):
5     return render(request,webqq/dashboard.html)
webqq views

2.表的建立,用户表就用BBS的用户表,添加字段即可,群组的表需要建立

技术分享
 1 from django.db import models
 2 from django.contrib.auth.models import User
 3 
 4 # Create your models here.
 5 
 6 
 7 
 8 class UserProifle(models.Model):
 9     user = models.OneToOneField(User,null=True,default=None)
10     name = models.CharField(max_length=32)
11     brief = models.CharField(max_length=128,default=这个人很懒,什么也没留下....)
12     friends = models.ManyToManyField(UserProifle,related_name=my_friends,blank=True,symmetrical=True)# 最后一个关键词是对称的意思
13                                                                                                             #我是你的朋友,你也是我的朋友
14 
15     def __str__(self):
16         return self.name
17 
18 class Article(models.Model):
19     """文章表"""
20     title = models.CharField(max_length=128,unique=True)
21     author = models.ForeignKey("UserProifle")
22     category = models.ForeignKey("Category")
23     pub_date = models.DateTimeField(auto_now_add=True,auto_created=True)
24     tags = models.ManyToManyField("Tag", null=True)
25     body = models.TextField(max_length=100000)
26     head_img = models.ImageField(upload_to="uploads")
27     status_choices = ((0,草稿),(1,发布),(2,隐藏))
28     priority = models.SmallIntegerField(default=1000,verbose_name="优先级")
29 
30     def __str__(self):
31         return self.title
32 
33 class Category(models.Model):
34     """板块"""
35     name = models.CharField(max_length=64,unique=True)
36     set_as_top_menu = models.BooleanField(default=True)
37 
38     def __str__(self):
39         return self.name
40 
41 
42 class Tag(models.Model):
43     """标签表"""
44     name = models.CharField(max_length=64, unique=True)
45     def __str__(self):
46         return self.name
47 
48 class Comment(models.Model):
49     """评论"""
50     article = models.ForeignKey("Article")
51     p_node = models.ForeignKey("Comment",null=True,blank=True,related_name="my_child_comments")
52 
53     user = models.ForeignKey("UserProifle")
54     date = models.DateTimeField(auto_now_add=True)
55     comment = models.TextField(max_length=1024)
56 
57 
58     def __str__(self):
59         return self.comment
60 
61 class Like(models.Model):
62     """点赞"""
63     article = models.ForeignKey("Article")
64     user = models.ForeignKey("UserProifle")
65     date = models.DateTimeField(auto_now_add=True)
66 
67 
68 class PrivateMail(models.Model):
69     """私信"""
70     pass
user表
技术分享
 1 from django.db import models
 2 from bbs.models import UserProifle
 3 
 4 # Create your models here.
 5 class QQGroup(models.Model):
 6     name = models.CharField(max_length=64)
 7     brief = models.CharField(max_length=128,blank=True)
 8     notificaton = models.CharField(max_length=128,blank=True,verbose_name=群公告)
 9     members = models.ManyToManyField(UserProifle)
10     max_members = models.PositiveSmallIntegerField(default=200)
11 
12     def __str__(self):
13         return self.name
group表
技术分享
1 from django.contrib import admin
2 
3 # Register your models here.
4 from webqq import models
5 admin.site.register(models.QQGroup)
webqq admin

如果想要admin中管理group表,需要添加到admin中。

 

 

<a href="#" class="list-group-item" contact_id ="{{ contact.id }}" onclick="OpenSession(this)">{{ contact }}</a> 

这句的作用是为了点击后高亮显示,通过绑定事件,this表示将自己传进去,让自己高亮,contact_id,contact_name是自定义属性,表示是谁打开的这个会话。(打开会话的同时,高亮显示),通过点击,将a标签中的数据传给OpenSession函数,执行就可以了。

技术分享
 1 {% extends index.html %}
 2 {% block extra-head-resources %}
 3     <link href="/static/css/webqq_style.css" rel="stylesheet">
 4 {% endblock %}
 5 {% block container %}
 6     <div class="row" style="margin-top: 10px">
 7         <div class="col-lg-2 contact_list">
 8             <ul class="nav nav-tabs" role="tablist">
 9               <li role="presentation"><a href="#contacts" aria-controls="profile" role="tab" data-toggle="tab">C</a></li>
10               <li role="presentation"><a href="#groups" aria-controls="profile" role="tab" data-toggle="tab">G</a></li>
11             </ul>
12             <div class="tab-content">
13                 <div role="tabpanel" class="tab-pane active" id="contacts">
14                     <div class="list-group">
15                         {% for contact in request.user.userproifle.friends.all %}
16                             <a href="#" class="list-group-item" contact_name ="{{ contact.name }}" contact_id ="{{ contact.id }}" onclick="OpenSession(this)">{{ contact }}
17                             <span class="badge">14</span></a>
18                         {% endfor %}
19                     </div>
20                 </div>
21                 <div role="tabpanel" class="tab-pane" id="groups">groups</div>
22             </div>
23 
24         </div>
25         <div class="col-lg-8 chat_panel">
26             <div class="row chat_panel_header">
27                 <div class="hidden" style="color: red">
28                     正在与<span id="chat_panel_header_text"></span>聊天
29                 </div>
30             </div>
31             <div class="row chat_panel_body">row chat_panel_body</div>
32             <div class="row chat_panel_input">row chat_panel_input</div>
33         </div>
34     </div>
35 {% endblock %}
36 {% block bottom-js %}
37     <script>
38         $(document).ready(function () {
39 
40         });
41         function OpenSession(ele) {
42            var contact_id = $(ele).attr(contact_id);
43            var contact_name = $(ele).attr(contact_name);
44            $(ele).addClass(active);
45            $(ele).siblings().removeClass(active);
46            $(chat_panel_header_text).text(contact_name);
47            $(chat_panel_header_text).parent().removeClass(hidden);
48 
49         }
50     </script>
51 {% endblock %}
View Code

 发送消息:

1.监控回车键,监控到发送消息

2.发送消息实现滚动,并在保留在最后一条信息。(animate就是实现动画)

技术分享
 1 {% extends index.html %}
 2 {% block extra-head-resources %}
 3     <link href="/static/css/webqq_style.css" rel="stylesheet">
 4 {% endblock %}
 5 {% block container %}
 6     <div class="row" style="margin-top: 10px">
 7         <div class="col-lg-2 contact_list">
 8             <ul class="nav nav-tabs" role="tablist">
 9               <li role="presentation"><a href="#contacts" aria-controls="profile" role="tab" data-toggle="tab">C</a></li>
10               <li role="presentation"><a href="#groups" aria-controls="profile" role="tab" data-toggle="tab">G</a></li>
11             </ul>
12             <div class="tab-content">
13                 <div role="tabpanel" class="tab-pane active" id="contacts">
14                     <div class="list-group">
15                         {% for contact in request.user.userproifle.friends.all %}
16                             <a href="#" class="list-group-item" contact_name ="{{ contact.name }}" contact_id ="{{ contact.id }}" onclick="OpenSession(this)">{{ contact }}
17                             <span class="badge">14</span></a>
18                         {% endfor %}
19                     </div>
20                 </div>
21                 <div role="tabpanel" class="tab-pane" id="groups">groups</div>
22             </div>
23 
24         </div>
25         <div class="col-lg-8 chat_panel">
26             <div class="row chat_panel_header">
27                 <div class="hidden" style="color: red">
28                     正在与<span id="chat_panel_header_text"></span>聊天
29                 </div>
30             </div>
31             <div class="row chat_panel_body">row chat_panel_body</div>
32             <div class="row chat_panel_input">
33                 <textarea id="msg_input_box" class="msg_input_box">
34 
35                 </textarea>
36             </div>
37         </div>
38     </div>
39 {% endblock %}
40 {% block bottom-js %}
41     <script>
42         $(document).ready(function () {
43 
44         });
45         $(body).delegate(textarea,keydown,function (e) {
46             if(e.which == 13) {   //回车键按下
47                 //发送消息,
48                 var msg_text = $(textarea).val();     //拿到消息
49                 if($.trim(msg_text).length > 0){            //判断是否有数据
50                     SendMsg(msg_text);
51                 }
52                 AddSentMsgIntoBox(msg_text);                  //把消息放到信息框中
53                 $(textarea).val(‘‘);                        //清空input框
54             }
55         });
56         function SendMsg(msg) {
57             console.log(going to send msg+ msg);
58         }
59         function AddSentMsgIntoBox(msg_text) {
60             var d = new Date();
61             var msg_ele = <div>  <div>{{ request.user.userproifle.name }} +d.getHours() +:+ d.getMinutes()+ :+d.getSeconds()+</div>;
62             msg_ele += <div> + msg_text + </div> </div>;
63             $(.chat_panel_body).append(msg_ele);
64             $(.chat_panel_body).animate({
65                 scrollTop:$(.chat_panel_body)[0].scrollHeight},500
66                 );
67         }
68         function OpenSession(ele) {
69            var contact_id = $(ele).attr(contact_id);
70            var contact_name = $(ele).attr(contact_name);
71            $(ele).addClass(active);
72            $(ele).siblings().removeClass(active);
73            $(chat_panel_header_text).text(contact_name);
74            $(chat_panel_header_text).parent().removeClass(hidden);
75 
76         }
77 
78 
79     </script>
80 {% endblock %}
dashboard.html
技术分享
 1 .contact_list{
 2     height: 600px;
 3     border: 1px dashed black;
 4 }
 5 .chat_panel{
 6     height:600px;
 7     border: 1px dashed red;
 8 }
 9 .chat_panel_header{
10     height: 50px;
11     border: 1px dashed orange;
12 }
13 .chat_panel_body{
14     height: 450px;
15     border: 1px dashed red;
16     overflow: auto;
17 }
18 .chat_panel_input{
19     height: 100px;
20     border: 1px dashed deeppink;
21 }
22 .msg_input_box{
23     height: 90px;
24     width: 90%;
25     resize: none;
26 }
webqq_style.css

发送到远程:

这就需要异步的知识,后台提交,页面不刷新(ajax)

做一个url,做数据处理,提交的数据是以post请求发过来的。(所有往后台提交的数据都是以post形式提交的)

在提交数据时,不应该写时间,要以服务器时间为准。

此处要在页面中写入csrf,否则无法提交,随便写在一个地方就可以。因为每发一条消息就要那一次,所以将提取csrf的函数写到全局就可以了。

from django.views.decorators.csrf import csrf_exempt    跳过验证

@csrf_exempt

技术分享
 1 {% extends index.html %}
 2 {% block extra-head-resources %}
 3     <link href="/static/css/webqq_style.css" rel="stylesheet">
 4 {% endblock %}
 5 {% block container %}
 6     <div class="row" style="margin-top: 10px">
 7     {% csrf_token %}
 8         <div class="col-lg-2 contact_list">
 9             <ul class="nav nav-tabs" role="tablist">
10               <li role="presentation"><a href="#contacts" aria-controls="profile" role="tab" data-toggle="tab">C</a></li>
11               <li role="presentation"><a href="#groups" aria-controls="profile" role="tab" data-toggle="tab">G</a></li>
12             </ul>
13             <div class="tab-content">
14                 <div role="tabpanel" class="tab-pane active" id="contacts">
15                     <div class="list-group">
16                         {% for contact in request.user.userproifle.friends.all %}
17                             <a href="#" class="list-group-item" contact_name ="{{ contact.name }}" contact_id ="{{ contact.id }}" onclick="OpenSession(this)">{{ contact }}
18                             <span class="badge">14</span></a>
19                         {% endfor %}
20                     </div>
21                 </div>
22                 <div role="tabpanel" class="tab-pane" id="groups">groups</div>
23             </div>
24 
25         </div>
26         <div class="col-lg-8 chat_panel">
27             <div class="row chat_panel_header">
28                 <div class="hidden" style="color: red">
29                     正在与<span contact_id = ‘‘ id="chat_panel_header_text"></span>聊天
30                 </div>
31             </div>
32             <div class="row chat_panel_body">row chat_panel_body</div>
33             <div class="row chat_panel_input">
34                 <textarea id="msg_input_box" class="msg_input_box">
35 
36                 </textarea>
37             </div>
38         </div>
39     </div>
40 {% endblock %}
41 {% block bottom-js %}
42     <script>
43         $(document).ready(function () {
44             csrfmiddlewaretoken  = $("input[name=‘csrfmiddlewaretoken‘]").val();
45         });
46         $(body).delegate(textarea,keydown,function (e) {
47             if(e.which == 13) {   //回车键按下
48                 //发送消息,
49                 var msg_text = $(textarea).val();     //拿到消息
50                 if($.trim(msg_text).length > 0){            //判断是否有数据
51                     SendMsg(msg_text);
52                 }
53                 AddSentMsgIntoBox(msg_text);                  //把消息放到信息框中
54                 $(textarea).val(‘‘);                        //清空input框
55             }
56         });
57         function SendMsg(msg) {
58             console.log(going to send msg+ msg);
59             var msg_data = {
60                 csrfmiddlewaretoken:csrfmiddlewaretoken,
61                 from:{{ request.user.userproifle.id}},
62                 to: $(chat_panel_header_text).attr(contact_id),
63                 data:msg
64 
65             }
66             $.post({% url msg_api %},msg_data,function(callback){
67                 console.log(callback);
68             });
69 
70 
71         }
72         function AddSentMsgIntoBox(msg_text) {
73             var d = new Date();
74             var msg_ele = <div>  <div>{{ request.user.userproifle.name }} +d.getHours() +:+ d.getMinutes()+ :+d.getSeconds()+</div>;
75             msg_ele += <div> + msg_text + </div> </div>;
76             $(.chat_panel_body).append(msg_ele);
77             $(.chat_panel_body).animate({
78                 scrollTop:$(.chat_panel_body)[0].scrollHeight},500
79                 );
80         }
81         function OpenSession(ele) {
82            var contact_id = $(ele).attr(contact_id);
83            var contact_name = $(ele).attr(contact_name);
84            $(ele).addClass(active);
85            $(ele).siblings().removeClass(active);
86            $(chat_panel_header_text).text(contact_name);
87            $(chat_panel_header_text).parent().removeClass(hidden);
88             $(chat_panel_header_text).attr(contact_id,contact_id)
89 
90         }
91 
92 
93     </script>
94 {% endblock %}
View Code
技术分享
1 def msg_api(request):
2      return HttpResponse(hello world)
url

将数据存起来(队列):

每个人都应该有单独的队列,队列的形式类似于字典,用户id对应用户消息

后台返回的字典,前端是可以直接用的,要转成json格式。 

技术分享
 1 {% extends index.html %}
 2 {% block extra-head-resources %}
 3     <link href="/static/css/webqq_style.css" rel="stylesheet">
 4 {% endblock %}
 5 {% block container %}
 6     <div class="row" style="margin-top: 10px">
 7     {% csrf_token %}
 8         <div class="col-lg-2 contact_list">
 9             <ul class="nav nav-tabs" role="tablist">
10               <li role="presentation"><a href="#contacts" aria-controls="profile" role="tab" data-toggle="tab">C</a></li>
11               <li role="presentation"><a href="#groups" aria-controls="profile" role="tab" data-toggle="tab">G</a></li>
12             </ul>
13             <div class="tab-content">
14                 <div role="tabpanel" class="tab-pane active" id="contacts">
15                     <div class="list-group">
16                         {% for contact in request.user.userproifle.friends.all %}
17                             <a href="#" class="list-group-item" contact_name ="{{ contact.name }}" contact_id ="{{ contact.id }}" onclick="OpenSession(this)">{{ contact }}
18                             <span class="badge">14</span></a>
19                         {% endfor %}
20                     </div>
21                 </div>
22                 <div role="tabpanel" class="tab-pane" id="groups">groups</div>
23             </div>
24 
25         </div>
26         <div class="col-lg-8 chat_panel">
27             <div class="row chat_panel_header">
28                 <div class="hidden" style="color: red">
29                     正在与<span contact_id = ‘‘ id="chat_panel_header_text"></span>聊天
30                 </div>
31             </div>
32             <div class="row chat_panel_body">row chat_panel_body</div>
33             <div class="row chat_panel_input">
34                 <textarea id="msg_input_box" class="msg_input_box">
35 
36                 </textarea>
37             </div>
38         </div>
39     </div>
40 {% endblock %}
41 {% block bottom-js %}
42     <script>
43         $(document).ready(function () {
44             csrfmiddlewaretoken  = $("input[name=‘csrfmiddlewaretoken‘]").val();
45         });
46         $(body).delegate(textarea,keydown,function (e) {
47             if(e.which == 13) {   //回车键按下
48                 //发送消息,
49                 var msg_text = $(textarea).val();     //拿到消息
50                 if($.trim(msg_text).length > 0){            //判断是否有数据
51                     SendMsg(msg_text);
52                 }
53                 AddSentMsgIntoBox(msg_text);                  //把消息放到信息框中
54                 $(textarea).val(‘‘);                        //清空input框
55             }
56         });
57         function SendMsg(msg) {
58             console.log(going to send msg+ msg);
59             var msg_data = {
60                 csrfmiddlewaretoken:csrfmiddlewaretoken,
61                 from:{{ request.user.userproifle.id}},
62                 to: $(chat_panel_header_text).attr(contact_id),
63                 data:msg
64 
65             }
66             $.post({% url msg_api %},msg_data,function(callback){
67                 var callback = JSON.parse(callback);  //json反序列化
68                 console.log(callback.msg_send_status);
69             });
70 
71 
72         }
73         function AddSentMsgIntoBox(msg_text) {
74             var d = new Date();
75             var msg_ele = <div>  <div>{{ request.user.userproifle.name }} +d.getHours() +:+ d.getMinutes()+ :+d.getSeconds()+</div>;
76             msg_ele += <div> + msg_text + </div> </div>;
77             $(.chat_panel_body).append(msg_ele);
78             $(.chat_panel_body).animate({
79                 scrollTop:$(.chat_panel_body)[0].scrollHeight},500
80                 );
81         }
82         function OpenSession(ele) {
83            var contact_id = $(ele).attr(contact_id);
84            var contact_name = $(ele).attr(contact_name);
85            $(ele).addClass(active);
86            $(ele).siblings().removeClass(active);
87            $(chat_panel_header_text).text(contact_name);
88            $(chat_panel_header_text).parent().removeClass(hidden);
89             $(chat_panel_header_text).attr(contact_id,contact_id)
90 
91         }
92 
93 
94     </script>
95 {% endblock %}
View Code
技术分享
 1 import datetime,time
 2 import queue
 3 class MsgHandler(object):
 4     def __init__(self,request,global_msg_queue):
 5         self.request = request
 6         self.global_msg_queue = global_msg_queue
 7 
 8     def get_msg_data(self):      #将数据转成特殊格式
 9         data= {}
10         data[from] = self.request.POST.get(from)
11         data[to] = self.request.POST.get(to)
12         data[data] = self.request.POST.get(data)
13         data[date] = time.time()
14         return data
15     def msg_send(self):
16         msg_data = self.get_msg_data()
17         if msg_data[to] not in self.global_msg_queue:
18             self.global_msg_queue[msg_data[to]] = queue.Queue
19         self.global_msg_queue[msg_data[to]].put(msg_data)
数据处理
技术分享
 1 from django.shortcuts import render,HttpResponse
 2 #from django.views.decorators.csrf import csrf_exempt    跳过验证
 3 import queue,json
 4 from webqq.msg_headler import MsgHandler
 5 # Create your views here.
 6 MSG_QUEUES = {}
 7 
 8 def dashboard(request):
 9     return render(request,webqq/dashboard.html)
10 #@csrf_exempt
11 def msg_api(request):
12 
13     msg_obj = MsgHandler(request,MSG_QUEUES)
14     msg_obj.msg_send()
15     return HttpResponse(json.dumps({msg_send_status:1}))
views

收消息(程序启动,不断的收消息):

 取消息是通过get方法

 取消息时,我们需要循环的去取数据,所以要做一个定时器,如果一直取得话,就会导致浏览器卡住,所以要hold住(如果队列中没有数据,你再去取数据的话就会卡住,通过这个来实现hold住)。

callback是拿到后端的返回值才会执行,如果后端没返回,就会自动卡住,所以不需要用定时器。

技术分享
  1 {% extends index.html %}
  2 {% block extra-head-resources %}
  3     <link href="/static/css/webqq_style.css" rel="stylesheet">
  4 {% endblock %}
  5 {% block container %}
  6     <div class="row" style="margin-top: 10px">
  7     {% csrf_token %}
  8         <div class="col-lg-2 contact_list">
  9             <ul class="nav nav-tabs" role="tablist">
 10               <li role="presentation"><a href="#contacts" aria-controls="profile" role="tab" data-toggle="tab">C</a></li>
 11               <li role="presentation"><a href="#groups" aria-controls="profile" role="tab" data-toggle="tab">G</a></li>
 12             </ul>
 13             <div class="tab-content">
 14                 <div role="tabpanel" class="tab-pane active" id="contacts">
 15                     <div class="list-group">
 16                         {% for contact in request.user.userproifle.friends.all %}
 17                             <a href="#" class="list-group-item" contact_name ="{{ contact.name }}" contact_id ="{{ contact.id }}" onclick="OpenSession(this)">{{ contact }}
 18                             <span class="badge">14</span></a>
 19                         {% endfor %}
 20                     </div>
 21                 </div>
 22                 <div role="tabpanel" class="tab-pane" id="groups">groups</div>
 23             </div>
 24 
 25         </div>
 26         <div class="col-lg-8 chat_panel">
 27             <div class="row chat_panel_header">
 28                 <div class="hidden" style="color: red">
 29                     正在与<span contact_id = ‘‘ id="chat_panel_header_text"></span>聊天
 30                 </div>
 31             </div>
 32             <div class="row chat_panel_body">row chat_panel_body</div>
 33             <div class="row chat_panel_input">
 34                 <textarea id="msg_input_box" class="msg_input_box">
 35 
 36                 </textarea>
 37             </div>
 38         </div>
 39     </div>
 40 {% endblock %}
 41 {% block bottom-js %}
 42     <script>
 43         $(document).ready(function () {
 44             csrfmiddlewaretoken  = $("input[name=‘csrfmiddlewaretoken‘]").val();
 45         });
 46         $(body).delegate(textarea,keydown,function (e) {
 47             if(e.which == 13) {   //回车键按下
 48                 //发送消息,
 49                 var msg_text = $(textarea).val();     //拿到消息
 50                 if($.trim(msg_text).length > 0){            //判断是否有数据
 51                     SendMsg(msg_text);
 52                 }
 53                 AddSentMsgIntoBox(msg_text);                  //把消息放到信息框中
 54                 $(textarea).val(‘‘);                        //清空input框
 55             }
 56 
 57 {#             setInterval(function () {#}
 58 {#                 LoadNewMsgs();#}
 59 {#             },10000)#}
 60         });
 61 
 62         LoadNewMsgs()
 63         function LoadNewMsgs() {
 64             $.get("{% url ‘get_msg‘ %}",function(callback){
 65                 console.log(get msg callback,callback);
 66                  return LoadNewMsgs();
 67             })
 68             
 69         }
 70         function SendMsg(msg) {
 71             console.log(going to send msg+ msg);
 72             var msg_data = {
 73                 csrfmiddlewaretoken:csrfmiddlewaretoken,
 74                 from:{{ request.user.userproifle.id}},
 75                 to: $(chat_panel_header_text).attr(contact_id),
 76                 data:msg
 77 
 78             }
 79             $.post({% url msg_api %},msg_data,function(callback){
 80                 var callback = JSON.parse(callback);  //json反序列化
 81                 console.log(callback.msg_send_status);
 82             });
 83 
 84 
 85         }
 86         function AddSentMsgIntoBox(msg_text) {
 87             var d = new Date();
 88             var msg_ele = <div>  <div>{{ request.user.userproifle.name }} +d.getHours() +:+ d.getMinutes()+ :+d.getSeconds()+</div>;
 89             msg_ele += <div> + msg_text + </div> </div>;
 90             $(.chat_panel_body).append(msg_ele);
 91             $(.chat_panel_body).animate({
 92                 scrollTop:$(.chat_panel_body)[0].scrollHeight},500
 93                 );
 94         }
 95         function OpenSession(ele) {
 96            var contact_id = $(ele).attr(contact_id);
 97            var contact_name = $(ele).attr(contact_name);
 98            $(ele).addClass(active);
 99            $(ele).siblings().removeClass(active);
100            $(chat_panel_header_text).text(contact_name);
101            $(chat_panel_header_text).parent().removeClass(hidden);
102             $(chat_panel_header_text).attr(contact_id,contact_id)
103 
104         }
105 
106 
107     </script>
108 {% endblock %}
dashboard.html
技术分享
 1 from django.shortcuts import render,HttpResponse
 2 #from django.views.decorators.csrf import csrf_exempt    跳过验证
 3 import queue,json
 4 from webqq.msg_headler import MsgHandler
 5 # Create your views here.
 6 MSG_QUEUES = {}
 7 
 8 def dashboard(request):
 9     return render(request,webqq/dashboard.html)
10 #@csrf_exempt
11 def msg_api(request):
12     msg_obj = MsgHandler(request, MSG_QUEUES)
13     if request.method == POST:
14         msg_obj.msg_send()
15         return HttpResponse(json.dumps({msg_send_status: 1}))
16     else:
17         msg_data = msg_obj.msg_recv()
18         return HttpResponse(json.dumps(msg_data))
views
技术分享
 1 import datetime,time
 2 import queue
 3 class MsgHandler(object):
 4     def __init__(self,request,global_msg_queue):
 5         self.request = request
 6         self.global_msg_queue = global_msg_queue
 7 
 8     def get_msg_data(self):      #将数据转成特殊格式
 9         data= {}
10         data[from] = self.request.POST.get(from)
11         data[to] = self.request.POST.get(to)
12         data[data] = self.request.POST.get(data)
13         data[date] = time.time()
14         return data
15     def msg_send(self):
16         msg_data = self.get_msg_data()
17         if msg_data[to] not in self.global_msg_queue:
18             self.global_msg_queue[msg_data[to]] = queue.Queue()
19         self.global_msg_queue[msg_data[to]].put(msg_data)
20 
21     def msg_recv(self):
22         user = self.request.user.userproifle
23         print(queue,self.global_msg_queue)
24         if str(user.id) in self.global_msg_queue:                   #前端拿到的是字符串,所以此处必须为字符串,这样才能匹配到
25             print(going to get message from queue for user id,user.id,user.name)
26             msg_data = self.global_msg_queue[str(user.id)].get()
27             return msg_data
28         else:
29             #create new queue for this user
30             self.global_msg_queue[str(user.id)] = queue.Queue()
31             msg_data = self.global_msg_queue[str(user.id)].get()
32 
33             return msg_data
msg_headler
技术分享
1 from django.conf.urls import url,include
2 from webqq import views
3 urlpatterns = [
4     url(rdashboard/$,views.dashboard,name=qq_dashboard),
5     url(rmsg_api/$,views.msg_api,name=msg_api),
6     url(rmsg_api/$,views.msg_api,name=get_msg),
7 ]
urls

 

















以上是关于WEB聊天的主要内容,如果未能解决你的问题,请参考以下文章

GitLab安装与汉化-实战

GitLab

如何从一组中继容器中组合片段?

在通知单击时启动片段而不会丢失状态

JAVA WEB代码片段

web代码片段