zoukankan      html  css  js  c++  java
  • WEB聊天

    功能需求:

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

    知识必备:

    1. django 
    2. htmlcssjs
    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(r'dashboard/$',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      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(r'dashboard/$',views.dashboard,name='qq_dashboard'),
    5     url(r'msg_api/$',views.msg_api,name='msg_api'),
    6     url(r'msg_api/$',views.msg_api,name='get_msg'),
    7 ]
    urls
  • 相关阅读:
    Git中使用.gitignore忽略文件的推送
    git stash详解
    Git撤销&回滚操作(git reset 和 get revert)
    git rebase和git merge的区别
    git撤销已经push到远程仓库上的代码
    Git Merge
    git cherry-pick 教程
    Failed to start LSB: Bring up/down错误解决方法
    linux centos7安装部署gitlab服务器
    CentOs7 HP找回root密码
  • 原文地址:https://www.cnblogs.com/l-w-q/p/6673728.html
Copyright © 2011-2022 走看看