WEB聊天
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了WEB聊天相关的知识,希望对你有一定的参考价值。
功能需求:
- 用户可以与好友一对一聊天
- 可以搜索、添加某人为好友
- 用户可以搜索和添加群
- 每个群有管理员可以审批用户的加群请求,群管理员可以用多个,群管理员可以删除、添加、禁言群友
- 可以与聊天室里的人进行临时会话(与qq群一样)
- 可以在群中发图片
- 可以与好友一对一发文件
知识必备:
- django
- html\\css\\js
- bootstrap
- 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 ]
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 ]
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 %}
自定义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 }
1 from django.shortcuts import render 2 3 # Create your views here. 4 def dashboard(request): 5 return render(request,‘webqq/dashboard.html‘)
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
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
1 from django.contrib import admin 2 3 # Register your models here. 4 from webqq import models 5 admin.site.register(models.QQGroup)
如果想要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 %}
发送消息:
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 %}
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 }
发送到远程:
这就需要异步的知识,后台提交,页面不刷新(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 %}
1 def msg_api(request): 2 return HttpResponse(‘hello world‘)
将数据存起来(队列):
每个人都应该有单独的队列,队列的形式类似于字典,用户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 %}
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}))
收消息(程序启动,不断的收消息):
取消息是通过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 %}
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))
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
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 ]
以上是关于WEB聊天的主要内容,如果未能解决你的问题,请参考以下文章