WEB聊天

功能需求:css

  1. 用戶能夠與好友一對一聊天
  2. 能夠搜索、添加某人爲好友
  3. 用戶能夠搜索和添加羣
  4. 每一個羣有管理員能夠審批用戶的加羣請求,羣管理員能夠用多個,羣管理員能夠刪除、添加、禁言羣友
  5. 能夠與聊天室裏的人進行臨時會話(與qq羣同樣)
  6. 能夠在羣中發圖片
  7. 能夠與好友一對一發文件

知識必備:html

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

 

前景介紹

首先咱們知道http是無狀態、請求/響應模式的通訊模式,就是用戶每次經過瀏覽器點擊一下頁面,都須要從新與WEB服務器創建一次鏈接,且發送本身的session id給服務器端以使服務器端驗證此用戶的身份。 客戶端若想從web服務器上獲取數據,必須主動發起一個請求,而後接收服務器端的返回,服務器端不會主動往客戶端推送消息。 前端

基於傳統的WEB服務器只會被動響應客戶端請求的這個認知,若是咱們想實現WEB實時聊天的需求,基本上只有如下幾種方式:html5

輪詢(polling)

輪詢 (Polling) 涉及了從客戶端向服務器端發出請求以獲取一些數據,這顯然就是一個純粹的 Ajax HTTP 請求。爲了儘快地得到服務器端事件,輪詢的間隔(兩次請求相隔的時間)必須儘量地小。但有這樣的一個缺點存在:若是間隔減少的話,客戶端瀏覽器就會發出更多的請求,這些請求中的許多都不會返回任何有用的數據,而這將會白白地浪費掉帶寬和處理資源。node

Comet

使用了輪詢的Ajax 很是受限:其不具伸縮性,不提供低延遲通訊(只要事件一到達服務器端,它們就以儘量快的速度到達瀏覽器端)。 Comet 是一個 Web 應用模型,在該模型中,請求被髮送到服務器端並保持一個很長的存活期,直到超時或是有服務器端事件發生。在該請求完成後,另外一個長生存期的 Ajax 請求就被送去等待另外一個服務器端事件。jquery

Comet 的一大優勢是,每一個客戶端始終都有一個向服務器端打開的通訊鏈路。服務器端能夠經過在事件到來時當即提交(完成)響應來把事件推給客戶端,或者它甚至能夠累積再連續發送。由於請求長時間保持打開的狀態,故服務器端須要特別的功能來處理全部的這些長生存期請求。web

 

Comet的實現主要有兩種方式:ajax

基於Ajax的長輪詢(long-polling)方式

瀏覽器發出XMLHttpRequest 請求,服務器端接收到請求後,會阻塞請求直到有數據或者超時才返回,瀏覽器JS在處理請求返回信息(超時或有效數據)後再次發出請求,從新創建鏈接。在此期間服務器端可能已經有新的數據到達,服務器會選擇把數據保存,直到從新創建鏈接,瀏覽器會把全部數據一次性取回。django

Forever Iframe(永存的 Iframe)

此技術涉及了一個置於頁面中的隱藏 Iframe 標籤,該標籤的 src 屬性指向返回服務器端事件的 servlet 路徑。每次在事件到達時,servlet 寫入並刷新一個新的 script 標籤,該標籤內部帶有 JavaScript 代碼,iframe 的內容被附加上這一 script 標籤,標籤中的內容就會獲得執行。json

 

 

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     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(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
相關文章
相關標籤/搜索