日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

django-web聊天

發(fā)布時(shí)間:2024/4/14 编程问答 30 豆豆
生活随笔 收集整理的這篇文章主要介紹了 django-web聊天 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

h2{ background-color: #00ccff; }

WEBQQ的實(shí)現(xiàn)的幾種方式

1、HTTP協(xié)議特點(diǎn)

首先這里要知道HTTP協(xié)議的特點(diǎn):短鏈接、無(wú)狀態(tài)!

在不考慮本地緩存的情況舉例來(lái)說(shuō):咱們?cè)谶B接博客園的時(shí)候,當(dāng)tcp連接后,我會(huì)把我自己的http頭發(fā)給博客園服務(wù)器,服務(wù)器端就會(huì)看到我請(qǐng)求的URL,server端就會(huì)根據(jù)URL分發(fā)到相應(yīng)的視圖處理(Django的views里)。最后給我返回這個(gè)頁(yè)面,當(dāng)返回之后連接就斷開(kāi)了。

短連接:

服務(wù)器為什么要斷開(kāi)?很多情況下我還會(huì)打開(kāi)頁(yè)面,我請(qǐng)求一次連接斷開(kāi)了為什么會(huì)這樣?為什么不建立長(zhǎng)期的連接?這個(gè)是HTTP設(shè)計(jì)的考慮,在大并發(fā)的情況下,如果連接不斷開(kāi),我不知道你什么時(shí)候點(diǎn),你可能立刻就點(diǎn)有可能10分鐘1個(gè)小時(shí)或者其他時(shí)間點(diǎn),那么你就會(huì)占著這個(gè)連接(是很浪費(fèi)的,并且連接是有限的),所以當(dāng)返回后server端就會(huì)斷開(kāi)這個(gè)連接。

無(wú)狀態(tài):

服務(wù)器不保存客戶端的任何狀態(tài),每一次客戶端連接服務(wù)器的時(shí)候都要把相關(guān)的信息發(fā)給客戶端告訴客戶端你是誰(shuí),服務(wù)端不會(huì)保存你是誰(shuí)?

那么問(wèn)題來(lái)了,為什么我們?cè)诘卿浘〇|之后登錄一次之后,服務(wù)器就不會(huì)讓咱們?cè)诘卿浟?/strong>,根據(jù)咱們之前的博客的Session和Cookie。服務(wù)器端會(huì)在用戶登錄的時(shí)候,在服務(wù)器端生成一個(gè)sessionID(有有效期)并且返回給客戶??蛻舳藭?huì)把這個(gè)seesionID存到Cookie里。這樣登錄之后就不需要再輸入密碼!

2、WEBqq通信實(shí)現(xiàn)

首先看下面的圖

根據(jù)WEBQQ的工作來(lái)看下,首先C1要發(fā)送一條數(shù)據(jù)給C2首先得通過(guò)WEB Server進(jìn)行中轉(zhuǎn),首先咱們這知道了,正常情況下當(dāng)C1發(fā)送給WEB Server之后,WEB Server就直接返回了,WEB Server就斷開(kāi)了C1的連接了,那么WEB Server會(huì)主動(dòng)給C2發(fā)送信息嗎?

WEB 服務(wù)器默認(rèn)是被動(dòng)接收請(qǐng)求的,如果你沒(méi)打開(kāi)瀏覽器,博客園可以給你發(fā)信息嗎?即便你打開(kāi)了瀏覽器,你獲取到數(shù)據(jù)之后就斷開(kāi)了,你看到的是本地緩存的數(shù)據(jù)。 你和服務(wù)器之間就沒(méi)有聯(lián)系了。如果服務(wù)器想把數(shù)據(jù)發(fā)送給C2那的等C2連接過(guò)來(lái),服務(wù)器一看有一條C2的數(shù)據(jù)然后發(fā)給C2.那么問(wèn)題又來(lái)了?他知道C2什么時(shí)候連接過(guò)來(lái)嗎?服務(wù)端不知道C2什么時(shí)候連接過(guò)來(lái)服務(wù)端又想能時(shí)時(shí)把數(shù)據(jù)發(fā)送給C2怎么做呢?《輪詢》

輪詢方式:

短輪詢:

C2客戶端有個(gè)循環(huán),去Server端取數(shù)據(jù)。不斷的循環(huán)去取(會(huì)對(duì)Server端造成壓力)

C2客戶端有個(gè)時(shí)間段的循環(huán),每隔1分鐘去取一次,但是不是時(shí)時(shí)的,這樣也不好。

長(zhǎng)輪詢:

上面的方式也是不可取的那怎么做呢:有沒(méi)有這么一種方法:當(dāng)C2請(qǐng)求過(guò)來(lái)接收的時(shí)候,Server端沒(méi)有C2的數(shù)據(jù),Server端沒(méi)有辦法主動(dòng)讓C2等著那怎么辦呢?把C2的請(qǐng)求掛起,當(dāng)有數(shù)據(jù)的時(shí)候在把數(shù)據(jù)立刻返回,并且多久還是沒(méi)有數(shù)據(jù)就把這個(gè)鏈接返回!

這樣所有的鏈接就變成有意義的請(qǐng)求。我不給他斷開(kāi)他就不會(huì)發(fā)新的請(qǐng)求!

本質(zhì)上還是輪詢,但是他發(fā)請(qǐng)求的頻率就非常低了!

但是有個(gè)問(wèn)題:他本質(zhì)上還是一個(gè)短鏈接(這里慢慢想下其實(shí)不難理解),如果消息頻繁的話,他還是不斷的重新建立鏈接。這樣也會(huì)對(duì)服務(wù)器造成影響!每收一條消息都得往返兩次。他其實(shí)也是不夠高效的。

真正的WEBQQ就是用的這個(gè)原理來(lái)實(shí)現(xiàn)的!(因?yàn)閃EB Socket只有部分瀏覽器支持(H5標(biāo)準(zhǔn))IE不支持,在中國(guó)的這個(gè)環(huán)境下IE使用率還是較高的所以不能普及,所以這個(gè)方法還是OK得)

?

還有一個(gè)方法就是,真正的長(zhǎng)連接,在瀏覽器上起一個(gè)Socket客戶端然后連接到服務(wù)端,他倆建立一個(gè)Socket通道,這樣就和Socket Server和Socket Client一樣這樣他們之間的數(shù)據(jù)傳輸就是,時(shí)時(shí)的了!這個(gè)就叫做WEB Socket ?!!!!!

Socket Server和Socket Client和WEB Socket的區(qū)別就是WEB Socket啟動(dòng)在瀏覽器上! 0 0 !

比如我們?cè)谥С諬5的瀏覽器上比如Google的瀏覽器輕松起一個(gè)WEB Socket,但是這個(gè)不僅僅要客戶端支持,Server端也得支持才可以!

sock = new WebSocket("ws://www.baidu.com")

WEB QQ 表結(jié)構(gòu)

?首先用戶的好友在哪個(gè)表里?在用戶表里那么他就的關(guān)聯(lián)自己了并且是多對(duì)多的關(guān)系,你可以有多個(gè)朋友,你朋友也可以有多個(gè)朋友!

class UserProfile(models.Model):'''用戶表'''#使用Django提供的用戶表,直接繼承就可以了.在原生的User表里擴(kuò)展!(原生的User表里就有用戶名和密碼)#一定要使用OneToOne,如果是正常的ForeignKey的話就表示User中的記錄可以對(duì)應(yīng)UserProfile中的多條記錄!#并且OneToOne的實(shí)現(xiàn)不是在SQL級(jí)別實(shí)現(xiàn)的而是在代碼基本實(shí)現(xiàn)的!user = models.OneToOneField(User)#名字name = models.CharField(max_length=32)#屬組groups = models.ManyToManyField("UserGroup")#朋友friends = models.ManyToManyField('self',related_name='my_friends')

然后在建立一個(gè)APP然后APP名稱為:web_chat 他調(diào)用WEB里的UserProfile用戶信息,然后在web_chat的models里新創(chuàng)建一個(gè)表:QQGroup!(復(fù)習(xí)不同APP間的Model調(diào)用~)

#/usr/bin/env python #-*- coding:utf-8 -*- from __future__ import unicode_literalsfrom django.db import models from web.models import UserProfile# Create your models here.class QQGroup(models.Model):'''QQ組表'''#組名name = models.CharField(max_length=64,unique=True)#注釋description = models.CharField(max_length=255,default="The Admin is so lazy,The Noting to show you ....")'''下面的members和admins在做跨APP關(guān)聯(lián)的時(shí)候,關(guān)聯(lián)的表不能使用雙引號(hào)!并且在調(diào)用,Django的User表的時(shí)候也不能加雙引號(hào)。'''#成員members = models.ManyToManyField(UserProfile,blank=True)#管理員admins = models.ManyToManyField(UserProfile,blank=True,related_name='group_admins')'''如果在一張表中,同樣調(diào)用了另一張表同樣的加related_name'''#最大成員數(shù)量max_member_nums = models.IntegerField(default=200)def __unicode__(self):return self.name

這里:members和admins在做跨APP關(guān)聯(lián)的時(shí)候,關(guān)聯(lián)的表不能使用雙引號(hào)!并且在調(diào)用,Django的User表的時(shí)候也不能加雙引號(hào)。

WEBQQ相關(guān)知識(shí)點(diǎn)總結(jié)

1、URL相關(guān)

在之前做不同APP的時(shí)候,我們都是輸入完全的URL,我們可以定義一個(gè)別名來(lái)使用它很方便!

別名的好處:如果說(shuō)那天想修改url里的這個(gè)url名稱了,是不是所有前端都得修改!并且在有好幾層的時(shí)候怎么改使用別名就會(huì)非常方便了!

projecet下的總URL

?

#!/usr/bin/env python #-*- coding:utf-8 -*- """Creazy_BBS URL ConfigurationThe `urlpatterns` list routes URLs to views. For more information please see:https://docs.djangoproject.com/en/1.9/topics/http/urls/ Examples: Function views1. Add an import: from my_app import views2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') Class-based views1. Add an import: from other_app.views import Home2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') Including another URLconf1. Import the include() function: from django.conf.urls import url, include2. Add a URL to urlpatterns: url(r'^blog/', include('blog.urls')) """ from django.conf.urls import url from django.conf.urls import include from django.contrib import adminfrom web import views from web import urls as web_urls from web_chat import urls as chat_urlsurlpatterns = [url(r'^admin/', admin.site.urls),#include-app weburl(r'^web/', include(web_urls)),#include-app web_chaturl(r'^chat/', include(chat_urls)),#指定默認(rèn)的URL,url(r'',views.index,name='index'), ]

web app中的URL指定相應(yīng)的別名

from django.conf.urls import url import views urlpatterns = [url(r'category/(\d+)/$',views.category,name='category'),url(r'article_detaill/(\d+)/$',views.article_detaill,name='article_detaill'),url(r'article/new/$',views.new_article,name='new_article'),url(r'account/logout$',views.acount_logout,name='logout'),url(r'account/login',views.acount_login,name='login'),]

web_chat app中的別名

from django.conf.urls import url import viewsurlpatterns = [url(r'^dashboard/$', views.dashboard,name='web_chat'),]

在前端引用的時(shí)候需要注意:例如下面兩個(gè)就需要使用別名來(lái)指定,格式也必須正確!

<li><a href="{% url 'new_article' %}">發(fā)帖</a></li> <li><a href="{% url 'logout' %}">用戶注銷</a></li>

2、使用Django自帶的模塊判斷用戶是否登錄

#/usr/bin/env python #-*- coding:utf-8 -*- from django.shortcuts import render#導(dǎo)入Django自帶的判斷用戶是否登錄的模塊 from django.contrib.auth.decorators import login_required # Create your views here.#應(yīng)用裝飾器 @login_required def dashboard(request):return render(request,'web_chat/dashboard.html')

然后在settings里配置,如果沒(méi)有登錄轉(zhuǎn)向的URL

LOGIN_URL = '/web/account/login/'

3、事件鏈

?

//頁(yè)面加載完成后$(document).ready(function () {//delegate 事件鏈,把多個(gè)事件進(jìn)行綁定//給body下的textarea進(jìn)行綁定,當(dāng)回車鍵按下后執(zhí)行的函數(shù)$("body").delegate("textarea", "keydown",function(e){if(e.which == 13) {//如果13這個(gè)按鍵(回車,可以通過(guò)console.log輸出實(shí)際按下的那個(gè)鍵),執(zhí)行下面的函數(shù)//send msg button clickedvar msg_text = $("textarea").val();if ($.trim(msg_text).length > 0){ //如果去除空格后,大于0//console.log(msg_text);//SendMsg(msg_text); //把數(shù)據(jù)進(jìn)行發(fā)送 }//把數(shù)據(jù)發(fā)送到聊天框里 AddSentMsgIntoBox(msg_text);$("textarea").val('');}});//end body});//頁(yè)面也在完成,結(jié)束

這里需要注意,在$(document).ready中調(diào)用的函數(shù)不能寫(xiě)在$(document).ready中,$(document).ready你已加載就執(zhí)行了,$(document).ready自己也是一個(gè)函數(shù),你$(document).ready執(zhí)行完之后就不存在了,就釋放了,你在$(document).ready中定義的函數(shù),外面就無(wú)法調(diào)用了。

4、聊天內(nèi)容自動(dòng)擴(kuò)展并且可以感覺(jué)內(nèi)容進(jìn)行自動(dòng)滑動(dòng)

?首先配置聊天的窗口樣式:

.chat_contener {width: 100%;height: 490px;background-color: black;opacity: 0.6;overflow: auto; }

然后配置,當(dāng)我們發(fā)送數(shù)據(jù)的時(shí)候自動(dòng)的滾動(dòng)

//定義發(fā)送到聊天框函數(shù)function AddSentMsgIntoBox(msg_text){//拼接聊天內(nèi)容/*氣泡實(shí)現(xiàn)<div class="clearfix"><div class="arrow"></div><div class="content_send"><div style="margin-top: 10px;margin-left: 5px;">Hello Shuaige</div></div></div>*/var msg_ele = "<div class='clearfix' style='padding-top:10px'>" + "<div class='arrow'>" + "</div>" +"<div class='content_send'>" + "<div style='margin-top: 10px;margin-left: 5px;'>" +msg_text + "</div>" + "</div>";$(".chat_contener").append(msg_ele);//animate 動(dòng)畫(huà)效果$('.chat_contener').animate({scrollTop: $('.chat_contener')[0].scrollHeight}, 500);//動(dòng)畫(huà)效果結(jié)束}//發(fā)送到聊天框函數(shù)結(jié)束

Ajax發(fā)送方式

正常情況下來(lái)說(shuō)咱們?cè)趯?xiě)一個(gè)Ajax請(qǐng)求的時(shí)候都是這么寫(xiě)的:

$.ajax({url:'/save_hostinfo/',type:'POST',tradition: true,data:{data:JSON.stringify(change_info)},success:function(arg){//成功接收的返回值(返回條目)var callback_dict = $.parseJSON(arg);//這里把字符串轉(zhuǎn)換為對(duì)象//然后咱們就可以判斷if(callback_dict){//執(zhí)行成功了//設(shè)置5秒鐘后隱藏setTimeout("hide()",5000);var change_infos = '修改了'+callback_dict['change_count']+'條數(shù)據(jù)';$('#handle_status').text(change_infos).removeClass('hide')}else{//如果為False執(zhí)行失敗了 alert(callback_dict.error)}}})

還有另一種方式(簡(jiǎn)約版):

//向后端發(fā)送數(shù)據(jù)$.post("{% url 'send_msg' %}" ,{'data':JSON.stringify(msg_dic)},function(callback){console.log(callback);});//向發(fā)送數(shù)據(jù)結(jié)束//解釋:// $.post 或者 $.get 是調(diào)用ajax方法//("URL路徑" ,{'data':JSON.stringify(msg_dic)},function(callback){})// // 這個(gè)第一個(gè)參數(shù)為指定的ULR 第二個(gè)參數(shù)為發(fā)送的內(nèi)容 第3個(gè)參數(shù)為回調(diào)函數(shù)和返回的值!!

AjaxPOST數(shù)據(jù)CSRF問(wèn)題?

在做Django的Form表單的時(shí)候?qū)W了,直接在提交表單哪里加上csrftoken就可以了,那Ajax怎么進(jìn)行認(rèn)證呢?可以使用下面的方法進(jìn)行認(rèn)證

//獲取CSRF參數(shù)function GetCsrfToken(){return $("input[name='csrfmiddlewaretoken']").val()}//發(fā)送消息function SendMsg(msg_text){var contact_id = $('#chat_hander h2').attr("contact_id"); //獲取發(fā)送給誰(shuí)消息var contact_type = $('#chat_hander h2').attr("contact_type");//獲取聊天類型var msg_dic = {'contact_type':contact_type,'to':contact_id,'from':"{{ request.user.userprofile.id }}",'from_name':"{{ request.user.userprofile.name }}",'msg':msg_text};//向后端發(fā)送數(shù)據(jù)$.post("{% url 'send_msg' %}" ,{'data':JSON.stringify(msg_dic),'csrfmiddlewaretoken':GetCsrfToken()},function(callback){console.log(callback);});//向發(fā)送數(shù)據(jù)結(jié)束//解釋:// $.post 或者 $.get 是調(diào)用ajax方法//("URL路徑" ,{'data':JSON.stringify(msg_dic)},function(callback){})// // 這個(gè)第一個(gè)參數(shù)為指定的ULR 第二個(gè)參數(shù)為發(fā)送的內(nèi)容 第3個(gè)參數(shù)為回調(diào)函數(shù)和返回的值!! }//發(fā)送消息結(jié)束

那有沒(méi)有一勞永逸的方式呢:

function getCookie(name) {var cookieValue = null;if (document.cookie && document.cookie != '') {var cookies = document.cookie.split(';');for (var i = 0; i < cookies.length; i++) {var cookie = jQuery.trim(cookies[i]);// Does this cookie string begin with the name we want?if (cookie.substring(0, name.length + 1) == (name + '=')) {cookieValue = decodeURIComponent(cookie.substring(name.length + 1));break;}}}return cookieValue; } var csrftoken = getCookie('csrftoken'); function csrfSafeMethod(method) {// these HTTP methods do not require CSRF protectionreturn (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method)); } $.ajaxSetup({beforeSend: function(xhr, settings) {if (!csrfSafeMethod(settings.type) && !this.crossDomain) {xhr.setRequestHeader("X-CSRFToken", csrftoken);}} });

還有一個(gè)插件,他實(shí)現(xiàn)了“一勞永逸的上半部分,下半部分還是得需要寫(xiě):JavaScript Cookie library?” ,其實(shí)也不是很多自己寫(xiě)的就可以了。

WEBQQ消息存儲(chǔ)方式?

首先要知道如下幾點(diǎn):C1發(fā)給C2消息,消息被發(fā)送到服務(wù)端之后,當(dāng)服務(wù)端請(qǐng)求過(guò)來(lái)之后C2接收到消息之后消息就服務(wù)端的數(shù)據(jù)就沒(méi)有意義了。所以不能使用Mysql、這樣的數(shù)據(jù)置于Redis和Memcache也是沒(méi)有必要的,當(dāng)然排除支持?jǐn)?shù)據(jù)夸不同設(shè)備可以把數(shù)據(jù)持久化!

那咱們?cè)趺醋瞿?#xff1f;想象一下數(shù)據(jù)被C2接收走之后,server端的數(shù)據(jù)就沒(méi)有意義了,用消息隊(duì)列方式是不是更好一點(diǎn)呢?

定義一個(gè)隊(duì)列,隊(duì)列不能寫(xiě)在接收函數(shù)哪里,寫(xiě)個(gè)全局的隊(duì)列即可,并且不能創(chuàng)建一個(gè)隊(duì)列,而是為每個(gè)用戶創(chuàng)建一個(gè)隊(duì)列。

import Queue GLOBAL_MQ = {}def new_msg(request):if request.method == 'POST':print request.POST.get('data')#獲取用戶發(fā)過(guò)來(lái)的數(shù)據(jù)data = json.loads(request.POST.get('data'))send_to = data['to']#判斷隊(duì)列里是否有這個(gè)用戶名,如果沒(méi)有新建一個(gè)隊(duì)列if send_to not in GLOBAL_MQ:GLOBAL_MQ[send_to] = Queue.Queue()data['timestamp'] = time.time()GLOBAL_MQ[send_to].put(data)return HttpResponse(GLOBAL_MQ[send_to].qsize())else:#因?yàn)殛?duì)列里目前存的是字符串所以我們需要先給他轉(zhuǎn)換為字符串request_user = str(request.user.userprofile.id)msg_lists = []#判斷是否在隊(duì)列里if request_user in GLOBAL_MQ: #判斷有多少條消息stored_msg_nums = GLOBAL_MQ[request_user].qsize()#把消息循環(huán)加入到列表中并發(fā)送for i in range(stored_msg_nums):msg_lists.append(GLOBAL_MQ[request_user].get())return HttpResponse(json.dumps(msg_lists))

使用Queue&JS實(shí)現(xiàn)長(zhǎng)輪詢

?先看下使用下面的方法是否可行:

#因?yàn)殛?duì)列里目前存的是字符串所以我們需要先給他轉(zhuǎn)換為字符串request_user = str(request.user.userprofile.id)msg_lists = []#判斷是否在隊(duì)列里if request_user in GLOBAL_MQ:#判斷有多少條消息stored_msg_nums = GLOBAL_MQ[request_user].qsize()#如果沒(méi)有新消息if stored_msg_nums == 0:print "\033[41;1m沒(méi)有消息等待,15秒.....\033[0m"msg_lists.append(GLOBAL_MQ[request_user].get())'''如果隊(duì)列里面有沒(méi)有消息,get就會(huì)阻塞,等待有新消息之后會(huì)繼續(xù)往下走,這里如果阻塞到這里了,等有新消息過(guò)來(lái)之后,把消息加入到msg_lists中后,for循環(huán)還是不執(zhí)行的因?yàn)?這個(gè)stored_msg_mums是在上面生成的變量下面for調(diào)用這個(gè)變量的時(shí)候他還是為0等返回之后再取得時(shí)候,現(xiàn)在stored_msg_nums不是0了,就執(zhí)行執(zhí)行for循環(huán)了,然后發(fā)送數(shù)據(jù)'''#把消息循環(huán)加入到列表中并發(fā)送print "\033[43;1等待已超時(shí)......15秒.....\033[0m"for i in range(stored_msg_nums):msg_lists.append(GLOBAL_MQ[request_user].get(timeout=15))else:#創(chuàng)建一個(gè)新隊(duì)列給這個(gè)用戶GLOBAL_MQ[str(request.user.userprofile.id)] = Queue.Queue()return HttpResponse(json.dumps(msg_lists))

但是為什么不等待不超時(shí)呢?反倒重復(fù)的進(jìn)行連接呢?我服務(wù)端不是已經(jīng)給他阻塞了嗎?


?這個(gè)上面的問(wèn)題就涉及到Client段的JS的:

//循環(huán)接收消息var RefreshNewMsgs = setInterval(function(){//接收消息 GetNewMsgs();},3000);

你每一次的的請(qǐng)求,都是一個(gè)新的線程,當(dāng)這個(gè)循環(huán)結(jié)束后自動(dòng)釋放但是,鏈接發(fā)到服務(wù)端就被阻塞了,過(guò)了一會(huì)setInterval又有一個(gè)新的連接向服務(wù)端,所以服務(wù)端每次阻塞的都是一個(gè)新的線程,就沒(méi)有實(shí)現(xiàn)咱們想要的效果!

setInterval每一次都新起一個(gè)線程!!!

那怎么解決這個(gè)問(wèn)題呢?自己調(diào)自己實(shí)現(xiàn)一個(gè)遞歸!

看代碼:

?

//接收消息function GetNewMsgs(){$.get("{% url 'get_new_msg' %}",function(callback){console.log("----->new msg:",callback);var msg_list = JSON.parse(callback);var current_open_session_id = $('#chat_hander h2').attr("contact_id");//獲取當(dāng)前打開(kāi)的IDvar current_open_session_type = $('#chat_hander h2').attr("contact_type");//獲取當(dāng)前打開(kāi)的類型,是單獨(dú)聊天還是群組聊天$.each(msg_list, function (index,msg_item) {//接收到的消息的to,是我自己 from是誰(shuí)發(fā)過(guò)來(lái)的,如果是當(dāng)前打開(kāi)的ID和from相同說(shuō)明,我現(xiàn)在正在和他聊天直接顯示即可if(msg_item.from == current_open_session_id){AddRecvMsgToChatBox(msg_item)}//判斷擋墻打開(kāi)ID接收 })})}//接收消息結(jié)束

GetNewMsgs是不是一個(gè)AJAX啊!他請(qǐng)求完之后會(huì)執(zhí)行一個(gè)回調(diào)函數(shù)啊!?這個(gè)回調(diào)函數(shù)執(zhí)行的時(shí)候是不是代表這個(gè)請(qǐng)求結(jié)束了在請(qǐng)求結(jié)束執(zhí)行這個(gè)回調(diào)函數(shù)的時(shí)候我在執(zhí)行以下GetNewMsgs()不就行了,又發(fā)起一個(gè)請(qǐng)求?

?

//接收消息function GetNewMsgs(){$.get("{% url 'get_new_msg' %}",function(callback){console.log("----->new msg:",callback);var msg_list = JSON.parse(callback);var current_open_session_id = $('#chat_hander h2').attr("contact_id");//獲取當(dāng)前打開(kāi)的IDvar current_open_session_type = $('#chat_hander h2').attr("contact_type");//獲取當(dāng)前打開(kāi)的類型,是單獨(dú)聊天還是群組聊天$.each(msg_list, function (index,msg_item) {//接收到的消息的to,是我自己 from是誰(shuí)發(fā)過(guò)來(lái)的,如果是當(dāng)前打開(kāi)的ID和from相同說(shuō)明,我現(xiàn)在正在和他聊天直接顯示即可if(msg_item.from == current_open_session_id){AddRecvMsgToChatBox(msg_item)}//判斷擋墻打開(kāi)ID接收});//結(jié)束循環(huán)console.log('run.....agin.....');GetNewMsgs(); })}//接收消息結(jié)束

然后把他加載到頁(yè)面加載完后自動(dòng)執(zhí)行中:

//循環(huán)接收消息GetNewMsgs();

Views函數(shù)也需要重新寫(xiě)下:(因?yàn)殛?duì)列里如果沒(méi)有數(shù)據(jù),設(shè)置為timeout的話就會(huì)拋異常,所以我們的抓異常~~)

代碼如下:

def new_msg(request):if request.method == 'POST':print request.POST.get('data')#獲取用戶發(fā)過(guò)來(lái)的數(shù)據(jù)data = json.loads(request.POST.get('data'))send_to = data['to']#判斷隊(duì)列里是否有這個(gè)用戶名,如果沒(méi)有新建一個(gè)隊(duì)列if send_to not in GLOBAL_MQ:GLOBAL_MQ[send_to] = Queue.Queue()data['timestamp'] = time.strftime("%Y-%m-%d %X", time.localtime())GLOBAL_MQ[send_to].put(data)return HttpResponse(GLOBAL_MQ[send_to].qsize())else:#因?yàn)殛?duì)列里目前存的是字符串所以我們需要先給他轉(zhuǎn)換為字符串request_user = str(request.user.userprofile.id)msg_lists = []#判斷是否在隊(duì)列里if request_user in GLOBAL_MQ:#判斷有多少條消息stored_msg_nums = GLOBAL_MQ[request_user].qsize()try:#如果沒(méi)有新消息if stored_msg_nums == 0:print "\033[41;1m沒(méi)有消息等待,15秒.....\033[0m"msg_lists.append(GLOBAL_MQ[request_user].get(timeout=15))'''如果隊(duì)列里面有沒(méi)有消息,get就會(huì)阻塞,等待有新消息之后會(huì)繼續(xù)往下走,這里如果阻塞到這里了,等有新消息過(guò)來(lái)之后,把消息加入到msg_lists中后,for循環(huán)還是不執(zhí)行的因?yàn)?這個(gè)stored_msg_mums是在上面生成的變量下面for調(diào)用這個(gè)變量的時(shí)候他還是為0等返回之后再取得時(shí)候,現(xiàn)在stored_msg_nums不是0了,就執(zhí)行執(zhí)行for循環(huán)了,然后發(fā)送數(shù)據(jù)'''except Exception as e:print ('error:',e)print "\033[43;1等待已超時(shí)......15秒.....\033[0m"# 把消息循環(huán)加入到列表中并發(fā)送for i in range(stored_msg_nums):msg_lists.append(GLOBAL_MQ[request_user].get())else:#創(chuàng)建一個(gè)新隊(duì)列給這個(gè)用戶GLOBAL_MQ[str(request.user.userprofile.id)] = Queue.Queue()return HttpResponse(json.dumps(msg_lists))

漂亮問(wèn)題解決:

消息實(shí)時(shí)效果實(shí)現(xiàn),NICE

這個(gè)在python中,如果這么遞歸,最多1000層,他的等前面的函數(shù)執(zhí)行完后退出!看下面的結(jié)果這個(gè)CallMyself(n+1)遞歸下面的print是永遠(yuǎn)不執(zhí)行的。

#!/usr/bin/env python #-*- coding:utf-8 -*- # Tim Luo LuoTianShuaidef CallMyself(n):print('level:',n)CallMyself(n+1)print('\033[32;1m測(cè)試輸出\033[0m')return 0CallMyself(1)

?

但是在JS中它不是這樣的,你會(huì)發(fā)現(xiàn)這個(gè)print還會(huì)執(zhí)行,說(shuō)面函數(shù)執(zhí)行完了。

?

頁(yè)面中的聊天框內(nèi)容,切換聊天人后聊天信息的存儲(chǔ)

有這么一種情況,現(xiàn)在我和ALEX聊天,我切換到和武Sir聊天了,但是窗口的內(nèi)容還在怎么辦?如下圖:

?怎么做呢?多層?如果200個(gè)人呢?

怎么做呢?

可以這樣,我在和Alex聊天的時(shí)候,切換到武Sir之后,把和Alex老師聊天內(nèi)容保存起來(lái),當(dāng)和武Sir結(jié)束聊天后,在返回來(lái)和Alex老師聊天的時(shí)候在把Alex老師內(nèi)容展現(xiàn),把和武Sir聊天內(nèi)容存起來(lái),其他亦如此!

?

//定義一個(gè)全局變量存儲(chǔ)用戶信息GLOBAL_SESSION_CACHE = {'single_contact':{},'group_contact':{},};//點(diǎn)擊用戶打開(kāi)連天窗口function OpenDialogBox(ele){//獲取與誰(shuí)聊天var contact_id = $(ele).attr("contact_id");var contact_name = $(ele).attr("chat_to");var contact_type = $(ele).attr("contact_type");//先把當(dāng)前聊天的內(nèi)容存儲(chǔ)起來(lái) DumpSession();//當(dāng)前聊天內(nèi)容存儲(chǔ)結(jié)束//修改聊天框與誰(shuí)聊天var chat_to_info = "<h2 style='color:whitesmoke;text-align:center;' contact_type='"+ contact_type +"' contact_id='"+ contact_id+ "'>" + contact_name + "</h2>";$('#chat_hander').html(chat_to_info);$('.chat_contener').html(LoadSession(contact_id,contact_type));//清除未讀消息顯示var unread_msg_num_ele = $(ele).find('span')[0];$(unread_msg_num_ele).text(0);$(unread_msg_num_ele).addClass('hide')}//打開(kāi)聊天窗口結(jié)束//存儲(chǔ)未打開(kāi)的聊天內(nèi)容function DumpSession2(contact_id,contact_type,content) {if(contact_id){GLOBAL_SESSION_CACHE[contact_type][contact_id] = content;}}//加載新的聊天窗口,把要打開(kāi)的聊天內(nèi)容重新加載上function LoadSession(current_contact_id,current_contact_type) {//通過(guò)hasOwnProperty判斷key是否存在if(GLOBAL_SESSION_CACHE[current_contact_type].hasOwnProperty(current_contact_id)){var session_html = GLOBAL_SESSION_CACHE[current_contact_type][current_contact_id];}else{var session_html = '';}//把內(nèi)容返回return session_html$('.chat_contener').html(session_html);};//加載新窗口結(jié)束

上述原理:把沒(méi)被當(dāng)前用戶打開(kāi)的消息界面時(shí),當(dāng)有好友發(fā)消息來(lái)了,就會(huì)先把內(nèi)容保存到字典里,也就是存到內(nèi)存里先,到打開(kāi)該好友時(shí)再html顯示。

?

?

?

?更多參考:http://www.cnblogs.com/alex3714/articles/5311625.html

?

轉(zhuǎn)載于:https://www.cnblogs.com/fengzaoye/p/5900647.html

總結(jié)

以上是生活随笔為你收集整理的django-web聊天的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。