功能需求:css
知識必備:html
首先咱們知道http是無狀態、請求/響應模式的通訊模式,就是用戶每次經過瀏覽器點擊一下頁面,都須要從新與WEB服務器創建一次鏈接,且發送本身的session id給服務器端以使服務器端驗證此用戶的身份。 客戶端若想從web服務器上獲取數據,必須主動發起一個請求,而後接收服務器端的返回,服務器端不會主動往客戶端推送消息。 前端
基於傳統的WEB服務器只會被動響應客戶端請求的這個認知,若是咱們想實現WEB實時聊天的需求,基本上只有如下幾種方式:html5
輪詢 (Polling) 涉及了從客戶端向服務器端發出請求以獲取一些數據,這顯然就是一個純粹的 Ajax HTTP 請求。爲了儘快地得到服務器端事件,輪詢的間隔(兩次請求相隔的時間)必須儘量地小。但有這樣的一個缺點存在:若是間隔減少的話,客戶端瀏覽器就會發出更多的請求,這些請求中的許多都不會返回任何有用的數據,而這將會白白地浪費掉帶寬和處理資源。node
使用了輪詢的Ajax 很是受限:其不具伸縮性,不提供低延遲通訊(只要事件一到達服務器端,它們就以儘量快的速度到達瀏覽器端)。 Comet 是一個 Web 應用模型,在該模型中,請求被髮送到服務器端並保持一個很長的存活期,直到超時或是有服務器端事件發生。在該請求完成後,另外一個長生存期的 Ajax 請求就被送去等待另外一個服務器端事件。jquery
Comet 的一大優勢是,每一個客戶端始終都有一個向服務器端打開的通訊鏈路。服務器端能夠經過在事件到來時當即提交(完成)響應來把事件推給客戶端,或者它甚至能夠累積再連續發送。由於請求長時間保持打開的狀態,故服務器端須要特別的功能來處理全部的這些長生存期請求。web
Comet的實現主要有兩種方式:ajax
瀏覽器發出XMLHttpRequest 請求,服務器端接收到請求後,會阻塞請求直到有數據或者超時才返回,瀏覽器JS在處理請求返回信息(超時或有效數據)後再次發出請求,從新創建鏈接。在此期間服務器端可能已經有新的數據到達,服務器會選擇把數據保存,直到從新創建鏈接,瀏覽器會把全部數據一次性取回。django
此技術涉及了一個置於頁面中的隱藏 Iframe 標籤,該標籤的 src
屬性指向返回服務器端事件的 servlet 路徑。每次在事件到達時,servlet 寫入並刷新一個新的 script 標籤,該標籤內部帶有 JavaScript 代碼,iframe 的內容被附加上這一 script 標籤,標籤中的內容就會獲得執行。json
若是說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 ]