django-web聊天
h2{ background-color: #00ccff; }
WEBQQ的實現(xiàn)的幾種方式
1、HTTP協(xié)議特點
首先這里要知道HTTP協(xié)議的特點:短鏈接、無狀態(tài)!
在不考慮本地緩存的情況舉例來說:咱們在連接博客園的時候,當tcp連接后,我會把我自己的http頭發(fā)給博客園服務器,服務器端就會看到我請求的URL,server端就會根據(jù)URL分發(fā)到相應的視圖處理(Django的views里)。最后給我返回這個頁面,當返回之后連接就斷開了。
短連接:
服務器為什么要斷開?很多情況下我還會打開頁面,我請求一次連接斷開了為什么會這樣?為什么不建立長期的連接?這個是HTTP設計的考慮,在大并發(fā)的情況下,如果連接不斷開,我不知道你什么時候點,你可能立刻就點有可能10分鐘1個小時或者其他時間點,那么你就會占著這個連接(是很浪費的,并且連接是有限的),所以當返回后server端就會斷開這個連接。
無狀態(tài):
服務器不保存客戶端的任何狀態(tài),每一次客戶端連接服務器的時候都要把相關的信息發(fā)給客戶端告訴客戶端你是誰,服務端不會保存你是誰?
那么問題來了,為什么我們在登錄京東之后登錄一次之后,服務器就不會讓咱們在登錄了,根據(jù)咱們之前的博客的Session和Cookie。服務器端會在用戶登錄的時候,在服務器端生成一個sessionID(有有效期)并且返回給客戶。客戶端會把這個seesionID存到Cookie里。這樣登錄之后就不需要再輸入密碼!
2、WEBqq通信實現(xiàn)
首先看下面的圖
根據(jù)WEBQQ的工作來看下,首先C1要發(fā)送一條數(shù)據(jù)給C2首先得通過WEB Server進行中轉,首先咱們這知道了,正常情況下當C1發(fā)送給WEB Server之后,WEB Server就直接返回了,WEB Server就斷開了C1的連接了,那么WEB Server會主動給C2發(fā)送信息嗎?
WEB 服務器默認是被動接收請求的,如果你沒打開瀏覽器,博客園可以給你發(fā)信息嗎?即便你打開了瀏覽器,你獲取到數(shù)據(jù)之后就斷開了,你看到的是本地緩存的數(shù)據(jù)。 你和服務器之間就沒有聯(lián)系了。如果服務器想把數(shù)據(jù)發(fā)送給C2那的等C2連接過來,服務器一看有一條C2的數(shù)據(jù)然后發(fā)給C2.那么問題又來了?他知道C2什么時候連接過來嗎?服務端不知道C2什么時候連接過來服務端又想能時時把數(shù)據(jù)發(fā)送給C2怎么做呢?《輪詢》
輪詢方式:
短輪詢:
C2客戶端有個循環(huán),去Server端取數(shù)據(jù)。不斷的循環(huán)去取(會對Server端造成壓力)
C2客戶端有個時間段的循環(huán),每隔1分鐘去取一次,但是不是時時的,這樣也不好。
長輪詢:
上面的方式也是不可取的那怎么做呢:有沒有這么一種方法:當C2請求過來接收的時候,Server端沒有C2的數(shù)據(jù),Server端沒有辦法主動讓C2等著那怎么辦呢?把C2的請求掛起,當有數(shù)據(jù)的時候在把數(shù)據(jù)立刻返回,并且多久還是沒有數(shù)據(jù)就把這個鏈接返回!
這樣所有的鏈接就變成有意義的請求。我不給他斷開他就不會發(fā)新的請求!
本質上還是輪詢,但是他發(fā)請求的頻率就非常低了!
但是有個問題:他本質上還是一個短鏈接(這里慢慢想下其實不難理解),如果消息頻繁的話,他還是不斷的重新建立鏈接。這樣也會對服務器造成影響!每收一條消息都得往返兩次。他其實也是不夠高效的。
真正的WEBQQ就是用的這個原理來實現(xiàn)的!(因為WEB Socket只有部分瀏覽器支持(H5標準)IE不支持,在中國的這個環(huán)境下IE使用率還是較高的所以不能普及,所以這個方法還是OK得)
?
還有一個方法就是,真正的長連接,在瀏覽器上起一個Socket客戶端然后連接到服務端,他倆建立一個Socket通道,這樣就和Socket Server和Socket Client一樣這樣他們之間的數(shù)據(jù)傳輸就是,時時的了!這個就叫做WEB Socket ?!!!!!
Socket Server和Socket Client和WEB Socket的區(qū)別就是WEB Socket啟動在瀏覽器上! 0 0 !
比如我們在支持H5的瀏覽器上比如Google的瀏覽器輕松起一個WEB Socket,但是這個不僅僅要客戶端支持,Server端也得支持才可以!
sock = new WebSocket("ws://www.baidu.com")WEB QQ 表結構
?首先用戶的好友在哪個表里?在用戶表里那么他就的關聯(lián)自己了并且是多對多的關系,你可以有多個朋友,你朋友也可以有多個朋友!
class UserProfile(models.Model):'''用戶表'''#使用Django提供的用戶表,直接繼承就可以了.在原生的User表里擴展!(原生的User表里就有用戶名和密碼)#一定要使用OneToOne,如果是正常的ForeignKey的話就表示User中的記錄可以對應UserProfile中的多條記錄!#并且OneToOne的實現(xiàn)不是在SQL級別實現(xiàn)的而是在代碼基本實現(xiàn)的!user = models.OneToOneField(User)#名字name = models.CharField(max_length=32)#屬組groups = models.ManyToManyField("UserGroup")#朋友friends = models.ManyToManyField('self',related_name='my_friends')然后在建立一個APP然后APP名稱為:web_chat 他調用WEB里的UserProfile用戶信息,然后在web_chat的models里新創(chuàng)建一個表:QQGroup!(復習不同APP間的Model調用~)
#/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關聯(lián)的時候,關聯(lián)的表不能使用雙引號!并且在調用,Django的User表的時候也不能加雙引號。'''#成員members = models.ManyToManyField(UserProfile,blank=True)#管理員admins = models.ManyToManyField(UserProfile,blank=True,related_name='group_admins')'''如果在一張表中,同樣調用了另一張表同樣的加related_name'''#最大成員數(shù)量max_member_nums = models.IntegerField(default=200)def __unicode__(self):return self.name這里:members和admins在做跨APP關聯(lián)的時候,關聯(lián)的表不能使用雙引號!并且在調用,Django的User表的時候也不能加雙引號。
WEBQQ相關知識點總結
1、URL相關
在之前做不同APP的時候,我們都是輸入完全的URL,我們可以定義一個別名來使用它很方便!
別名的好處:如果說那天想修改url里的這個url名稱了,是不是所有前端都得修改!并且在有好幾層的時候怎么改使用別名就會非常方便了!
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)),#指定默認的URL,url(r'',views.index,name='index'), ]web app中的URL指定相應的別名
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'),]在前端引用的時候需要注意:例如下面兩個就需要使用別名來指定,格式也必須正確!
<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#導入Django自帶的判斷用戶是否登錄的模塊 from django.contrib.auth.decorators import login_required # Create your views here.#應用裝飾器 @login_required def dashboard(request):return render(request,'web_chat/dashboard.html')然后在settings里配置,如果沒有登錄轉向的URL
LOGIN_URL = '/web/account/login/'3、事件鏈
?
//頁面加載完成后$(document).ready(function () {//delegate 事件鏈,把多個事件進行綁定//給body下的textarea進行綁定,當回車鍵按下后執(zhí)行的函數(shù)$("body").delegate("textarea", "keydown",function(e){if(e.which == 13) {//如果13這個按鍵(回車,可以通過console.log輸出實際按下的那個鍵),執(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ù)進行發(fā)送 }//把數(shù)據(jù)發(fā)送到聊天框里 AddSentMsgIntoBox(msg_text);$("textarea").val('');}});//end body});//頁面也在完成,結束這里需要注意,在$(document).ready中調用的函數(shù)不能寫在$(document).ready中,$(document).ready你已加載就執(zhí)行了,$(document).ready自己也是一個函數(shù),你$(document).ready執(zhí)行完之后就不存在了,就釋放了,你在$(document).ready中定義的函數(shù),外面就無法調用了。
4、聊天內容自動擴展并且可以感覺內容進行自動滑動
?首先配置聊天的窗口樣式:
.chat_contener {width: 100%;height: 490px;background-color: black;opacity: 0.6;overflow: auto; }然后配置,當我們發(fā)送數(shù)據(jù)的時候自動的滾動
//定義發(fā)送到聊天框函數(shù)function AddSentMsgIntoBox(msg_text){//拼接聊天內容/*氣泡實現(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 動畫效果$('.chat_contener').animate({scrollTop: $('.chat_contener')[0].scrollHeight}, 500);//動畫效果結束}//發(fā)送到聊天框函數(shù)結束Ajax發(fā)送方式
正常情況下來說咱們在寫一個Ajax請求的時候都是這么寫的:
還有另一種方式(簡約版):
//向后端發(fā)送數(shù)據(jù)$.post("{% url 'send_msg' %}" ,{'data':JSON.stringify(msg_dic)},function(callback){console.log(callback);});//向發(fā)送數(shù)據(jù)結束//解釋:// $.post 或者 $.get 是調用ajax方法//("URL路徑" ,{'data':JSON.stringify(msg_dic)},function(callback){})// // 這個第一個參數(shù)為指定的ULR 第二個參數(shù)為發(fā)送的內容 第3個參數(shù)為回調函數(shù)和返回的值!!AjaxPOST數(shù)據(jù)CSRF問題?
在做Django的Form表單的時候學了,直接在提交表單哪里加上csrftoken就可以了,那Ajax怎么進行認證呢?可以使用下面的方法進行認證
//獲取CSRF參數(shù)function GetCsrfToken(){return $("input[name='csrfmiddlewaretoken']").val()}//發(fā)送消息function SendMsg(msg_text){var contact_id = $('#chat_hander h2').attr("contact_id"); //獲取發(fā)送給誰消息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ù)結束//解釋:// $.post 或者 $.get 是調用ajax方法//("URL路徑" ,{'data':JSON.stringify(msg_dic)},function(callback){})// // 這個第一個參數(shù)為指定的ULR 第二個參數(shù)為發(fā)送的內容 第3個參數(shù)為回調函數(shù)和返回的值!! }//發(fā)送消息結束那有沒有一勞永逸的方式呢:
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);}} });還有一個插件,他實現(xiàn)了“一勞永逸的上半部分,下半部分還是得需要寫:JavaScript Cookie library?” ,其實也不是很多自己寫的就可以了。
WEBQQ消息存儲方式?
首先要知道如下幾點:C1發(fā)給C2消息,消息被發(fā)送到服務端之后,當服務端請求過來之后C2接收到消息之后消息就服務端的數(shù)據(jù)就沒有意義了。所以不能使用Mysql、這樣的數(shù)據(jù)置于Redis和Memcache也是沒有必要的,當然排除支持數(shù)據(jù)夸不同設備可以把數(shù)據(jù)持久化!
那咱們怎么做呢?想象一下數(shù)據(jù)被C2接收走之后,server端的數(shù)據(jù)就沒有意義了,用消息隊列方式是不是更好一點呢?
定義一個隊列,隊列不能寫在接收函數(shù)哪里,寫個全局的隊列即可,并且不能創(chuàng)建一個隊列,而是為每個用戶創(chuàng)建一個隊列。
import Queue GLOBAL_MQ = {}def new_msg(request):if request.method == 'POST':print request.POST.get('data')#獲取用戶發(fā)過來的數(shù)據(jù)data = json.loads(request.POST.get('data'))send_to = data['to']#判斷隊列里是否有這個用戶名,如果沒有新建一個隊列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:#因為隊列里目前存的是字符串所以我們需要先給他轉換為字符串request_user = str(request.user.userprofile.id)msg_lists = []#判斷是否在隊列里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實現(xiàn)長輪詢
?先看下使用下面的方法是否可行:
#因為隊列里目前存的是字符串所以我們需要先給他轉換為字符串request_user = str(request.user.userprofile.id)msg_lists = []#判斷是否在隊列里if request_user in GLOBAL_MQ:#判斷有多少條消息stored_msg_nums = GLOBAL_MQ[request_user].qsize()#如果沒有新消息if stored_msg_nums == 0:print "\033[41;1m沒有消息等待,15秒.....\033[0m"msg_lists.append(GLOBAL_MQ[request_user].get())'''如果隊列里面有沒有消息,get就會阻塞,等待有新消息之后會繼續(xù)往下走,這里如果阻塞到這里了,等有新消息過來之后,把消息加入到msg_lists中后,for循環(huán)還是不執(zhí)行的因為,這個stored_msg_mums是在上面生成的變量下面for調用這個變量的時候他還是為0等返回之后再取得時候,現(xiàn)在stored_msg_nums不是0了,就執(zhí)行執(zhí)行for循環(huán)了,然后發(fā)送數(shù)據(jù)'''#把消息循環(huán)加入到列表中并發(fā)送print "\033[43;1等待已超時......15秒.....\033[0m"for i in range(stored_msg_nums):msg_lists.append(GLOBAL_MQ[request_user].get(timeout=15))else:#創(chuàng)建一個新隊列給這個用戶GLOBAL_MQ[str(request.user.userprofile.id)] = Queue.Queue()return HttpResponse(json.dumps(msg_lists))但是為什么不等待不超時呢?反倒重復的進行連接呢?我服務端不是已經(jīng)給他阻塞了嗎?
?這個上面的問題就涉及到Client段的JS的:
//循環(huán)接收消息var RefreshNewMsgs = setInterval(function(){//接收消息 GetNewMsgs();},3000);你每一次的的請求,都是一個新的線程,當這個循環(huán)結束后自動釋放但是,鏈接發(fā)到服務端就被阻塞了,過了一會setInterval又有一個新的連接向服務端,所以服務端每次阻塞的都是一個新的線程,就沒有實現(xiàn)咱們想要的效果!
setInterval每一次都新起一個線程!!!
那怎么解決這個問題呢?自己調自己實現(xiàn)一個遞歸!
看代碼:
?
//接收消息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");//獲取當前打開的IDvar current_open_session_type = $('#chat_hander h2').attr("contact_type");//獲取當前打開的類型,是單獨聊天還是群組聊天$.each(msg_list, function (index,msg_item) {//接收到的消息的to,是我自己 from是誰發(fā)過來的,如果是當前打開的ID和from相同說明,我現(xiàn)在正在和他聊天直接顯示即可if(msg_item.from == current_open_session_id){AddRecvMsgToChatBox(msg_item)}//判斷擋墻打開ID接收 })})}//接收消息結束GetNewMsgs是不是一個AJAX啊!他請求完之后會執(zhí)行一個回調函數(shù)啊!?這個回調函數(shù)執(zhí)行的時候是不是代表這個請求結束了?在請求結束執(zhí)行這個回調函數(shù)的時候我在執(zhí)行以下GetNewMsgs()不就行了,又發(fā)起一個請求?
?
//接收消息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");//獲取當前打開的IDvar current_open_session_type = $('#chat_hander h2').attr("contact_type");//獲取當前打開的類型,是單獨聊天還是群組聊天$.each(msg_list, function (index,msg_item) {//接收到的消息的to,是我自己 from是誰發(fā)過來的,如果是當前打開的ID和from相同說明,我現(xiàn)在正在和他聊天直接顯示即可if(msg_item.from == current_open_session_id){AddRecvMsgToChatBox(msg_item)}//判斷擋墻打開ID接收});//結束循環(huán)console.log('run.....agin.....');GetNewMsgs(); })}//接收消息結束然后把他加載到頁面加載完后自動執(zhí)行中:
//循環(huán)接收消息GetNewMsgs();Views函數(shù)也需要重新寫下:(因為隊列里如果沒有數(shù)據(jù),設置為timeout的話就會拋異常,所以我們的抓異常~~)
代碼如下:
def new_msg(request):if request.method == 'POST':print request.POST.get('data')#獲取用戶發(fā)過來的數(shù)據(jù)data = json.loads(request.POST.get('data'))send_to = data['to']#判斷隊列里是否有這個用戶名,如果沒有新建一個隊列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:#因為隊列里目前存的是字符串所以我們需要先給他轉換為字符串request_user = str(request.user.userprofile.id)msg_lists = []#判斷是否在隊列里if request_user in GLOBAL_MQ:#判斷有多少條消息stored_msg_nums = GLOBAL_MQ[request_user].qsize()try:#如果沒有新消息if stored_msg_nums == 0:print "\033[41;1m沒有消息等待,15秒.....\033[0m"msg_lists.append(GLOBAL_MQ[request_user].get(timeout=15))'''如果隊列里面有沒有消息,get就會阻塞,等待有新消息之后會繼續(xù)往下走,這里如果阻塞到這里了,等有新消息過來之后,把消息加入到msg_lists中后,for循環(huán)還是不執(zhí)行的因為,這個stored_msg_mums是在上面生成的變量下面for調用這個變量的時候他還是為0等返回之后再取得時候,現(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等待已超時......15秒.....\033[0m"# 把消息循環(huán)加入到列表中并發(fā)送for i in range(stored_msg_nums):msg_lists.append(GLOBAL_MQ[request_user].get())else:#創(chuàng)建一個新隊列給這個用戶GLOBAL_MQ[str(request.user.userprofile.id)] = Queue.Queue()return HttpResponse(json.dumps(msg_lists))漂亮問題解決:
消息實時效果實現(xiàn),NICE
這個在python中,如果這么遞歸,最多1000層,他的等前面的函數(shù)執(zhí)行完后退出!看下面的結果這個CallMyself(n+1)遞歸下面的print是永遠不執(zhí)行的。
#!/usr/bin/env python #-*- coding:utf-8 -*- # Tim Luo LuoTianShuaidef CallMyself(n):print('level:',n)CallMyself(n+1)print('\033[32;1m測試輸出\033[0m')return 0CallMyself(1)?
但是在JS中它不是這樣的,你會發(fā)現(xiàn)這個print還會執(zhí)行,說面函數(shù)執(zhí)行完了。
?
頁面中的聊天框內容,切換聊天人后聊天信息的存儲
有這么一種情況,現(xiàn)在我和ALEX聊天,我切換到和武Sir聊天了,但是窗口的內容還在怎么辦?如下圖:
?怎么做呢?多層?如果200個人呢?
怎么做呢?
可以這樣,我在和Alex聊天的時候,切換到武Sir之后,把和Alex老師聊天內容保存起來,當和武Sir結束聊天后,在返回來和Alex老師聊天的時候在把Alex老師內容展現(xiàn),把和武Sir聊天內容存起來,其他亦如此!
?
//定義一個全局變量存儲用戶信息GLOBAL_SESSION_CACHE = {'single_contact':{},'group_contact':{},};//點擊用戶打開連天窗口function OpenDialogBox(ele){//獲取與誰聊天var contact_id = $(ele).attr("contact_id");var contact_name = $(ele).attr("chat_to");var contact_type = $(ele).attr("contact_type");//先把當前聊天的內容存儲起來 DumpSession();//當前聊天內容存儲結束//修改聊天框與誰聊天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')}//打開聊天窗口結束//存儲未打開的聊天內容function DumpSession2(contact_id,contact_type,content) {if(contact_id){GLOBAL_SESSION_CACHE[contact_type][contact_id] = content;}}//加載新的聊天窗口,把要打開的聊天內容重新加載上function LoadSession(current_contact_id,current_contact_type) {//通過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 = '';}//把內容返回return session_html$('.chat_contener').html(session_html);};//加載新窗口結束上述原理:把沒被當前用戶打開的消息界面時,當有好友發(fā)消息來了,就會先把內容保存到字典里,也就是存到內存里先,到打開該好友時再html顯示。
?
?
?
?更多參考:http://www.cnblogs.com/alex3714/articles/5311625.html
?
轉載于:https://www.cnblogs.com/fengzaoye/p/5900647.html
總結
以上是生活随笔為你收集整理的django-web聊天的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python 入门学习篇(一),呵呵呵
- 下一篇: Codeforces Round #10