h2{ background-color: #00ccff; }

WEBQQ的实现的几种方式

1、HTTP协议特点

首先这里要知道HTTP协议的特点:短链接、无状态!

在不考虑本地缓存的情况举例来说:咱们在连接博客园的时候,当tcp连接后,我会把我自己的http头发给博客园服务器,服务器端就会看到我请求的URL,server端就会根据URL分发到相应的视图处理(Django的views里)。最后给我返回这个页面,当返回之后连接就断开了。

短连接:

服务器为什么要断开?很多情况下我还会打开页面,我请求一次连接断开了为什么会这样?为什么不建立长期的连接?这个是HTTP设计的考虑,在大并发的情况下,如果连接不断开,我不知道你什么时候点,你可能立刻就点有可能10分钟1个小时或者其他时间点,那么你就会占着这个连接(是很浪费的,并且连接是有限的),所以当返回后server端就会断开这个连接。

无状态:

服务器不保存客户端的任何状态,每一次客户端连接服务器的时候都要把相关的信息发给客户端告诉客户端你是谁,服务端不会保存你是谁?

那么问题来了,为什么我们在登录京东之后登录一次之后,服务器就不会让咱们在登录了,根据咱们之前的博客的Session和Cookie。服务器端会在用户登录的时候,在服务器端生成一个sessionID(有有效期)并且返回给客户。客户端会把这个seesionID存到Cookie里。这样登录之后就不需要再输入密码!

2、WEBqq通信实现

首先看下面的图

根据WEBQQ的工作来看下,首先C1要发送一条数据给C2首先得通过WEB Server进行中转,首先咱们这知道了,正常情况下当C1发送给WEB Server之后,WEB Server就直接返回了,WEB Server就断开了C1的连接了,那么WEB Server会主动给C2发送信息吗?

WEB 服务器默认是被动接收请求的,如果你没打开浏览器,博客园可以给你发信息吗?即便你打开了浏览器,你获取到数据之后就断开了,你看到的是本地缓存的数据。 你和服务器之间就没有联系了。如果服务器想把数据发送给C2那的等C2连接过来,服务器一看有一条C2的数据然后发给C2.那么问题又来了?他知道C2什么时候连接过来吗?服务端不知道C2什么时候连接过来服务端又想能时时把数据发送给C2怎么做呢?《轮询》

轮询方式:

短轮询:

C2客户端有个循环,去Server端取数据。不断的循环去取(会对Server端造成压力)

C2客户端有个时间段的循环,每隔1分钟去取一次,但是不是时时的,这样也不好。

长轮询:

上面的方式也是不可取的那怎么做呢:有没有这么一种方法:当C2请求过来接收的时候,Server端没有C2的数据,Server端没有办法主动让C2等着那怎么办呢?把C2的请求挂起,当有数据的时候在把数据立刻返回,并且多久还是没有数据就把这个链接返回!

这样所有的链接就变成有意义的请求。我不给他断开他就不会发新的请求!

本质上还是轮询,但是他发请求的频率就非常低了!

但是有个问题:他本质上还是一个短链接(这里慢慢想下其实不难理解),如果消息频繁的话,他还是不断的重新建立链接。这样也会对服务器造成影响!每收一条消息都得往返两次。他其实也是不够高效的。

真正的WEBQQ就是用的这个原理来实现的!(因为WEB Socket只有部分浏览器支持(H5标准)IE不支持,在中国的这个环境下IE使用率还是较高的所以不能普及,所以这个方法还是OK得)

还有一个方法就是,真正的长连接,在浏览器上起一个Socket客户端然后连接到服务端,他俩建立一个Socket通道,这样就和Socket Server和Socket Client一样这样他们之间的数据传输就是,时时的了!这个就叫做WEB Socket  !!!!!

Socket Server和Socket Client和WEB Socket的区别就是WEB Socket启动在浏览器上! 0 0 !

比如我们在支持H5的浏览器上比如Google的浏览器轻松起一个WEB Socket,但是这个不仅仅要客户端支持,Server端也得支持才可以!

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

WEB QQ 表结构

首先用户的好友在哪个表里?在用户表里那么他就的关联自己了并且是多对多的关系,你可以有多个朋友,你朋友也可以有多个朋友!

class UserProfile(models.Model):'''用户表'''#使用Django提供的用户表,直接继承就可以了.在原生的User表里扩展!(原生的User表里就有用户名和密码)#一定要使用OneToOne,如果是正常的ForeignKey的话就表示User中的记录可以对应UserProfile中的多条记录!#并且OneToOne的实现不是在SQL级别实现的而是在代码基本实现的!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里新创建一个表: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关联的时候,关联的表不能使用双引号!并且在调用,Django的User表的时候也不能加双引号。'''#成员members = models.ManyToManyField(UserProfile,blank=True)#管理员admins = models.ManyToManyField(UserProfile,blank=True,related_name='group_admins')'''如果在一张表中,同样调用了另一张表同样的加related_name'''#最大成员数量max_member_nums = models.IntegerField(default=200)def __unicode__(self):return self.name

这里:members和admins在做跨APP关联的时候,关联的表不能使用双引号!并且在调用,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' %}">发帖</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进行绑定,当回车键按下后执行的函数$("body").delegate("textarea", "keydown",function(e){if(e.which == 13) {//如果13这个按键(回车,可以通过console.log输出实际按下的那个键),执行下面的函数//send msg button clickedvar msg_text = $("textarea").val();if ($.trim(msg_text).length > 0){ //如果去除空格后,大于0//console.log(msg_text);//SendMsg(msg_text); //把数据进行发送
                            }//把数据发送到聊天框里
                            AddSentMsgIntoBox(msg_text);$("textarea").val('');}});//end body});//页面也在完成,结束

这里需要注意,在$(document).ready中调用的函数不能写在$(document).ready中,$(document).ready你已加载就执行了,$(document).ready自己也是一个函数,你$(document).ready执行完之后就不存在了,就释放了,你在$(document).ready中定义的函数,外面就无法调用了。

4、聊天内容自动扩展并且可以感觉内容进行自动滑动

首先配置聊天的窗口样式:

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

然后配置,当我们发送数据的时候自动的滚动

//定义发送到聊天框函数function AddSentMsgIntoBox(msg_text){//拼接聊天内容/*气泡实现<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);//动画效果结束}//发送到聊天框函数结束

Ajax发送方式

正常情况下来说咱们在写一个Ajax请求的时候都是这么写的:

$.ajax({url:'/save_hostinfo/',type:'POST',tradition: true,data:{data:JSON.stringify(change_info)},success:function(arg){//成功接收的返回值(返回条目)var callback_dict = $.parseJSON(arg);//这里把字符串转换为对象//然后咱们就可以判断if(callback_dict){//执行成功了//设置5秒钟后隐藏setTimeout("hide()",5000);var change_infos = '修改了'+callback_dict['change_count']+'条数据';$('#handle_status').text(change_infos).removeClass('hide')}else{//如果为False执行失败了
                        alert(callback_dict.error)}}})

还有另一种方式(简约版):

//向后端发送数据$.post("{% url 'send_msg' %}" ,{'data':JSON.stringify(msg_dic)},function(callback){console.log(callback);});//向发送数据结束//解释:// $.post 或者 $.get 是调用ajax方法//("URL路径" ,{'data':JSON.stringify(msg_dic)},function(callback){})// // 这个第一个参数为指定的ULR 第二个参数为发送的内容 第3个参数为回调函数和返回的值!!

AjaxPOST数据CSRF问题

在做Django的Form表单的时候学了,直接在提交表单哪里加上csrftoken就可以了,那Ajax怎么进行认证呢?可以使用下面的方法进行认证

//获取CSRF参数function GetCsrfToken(){return $("input[name='csrfmiddlewaretoken']").val()}//发送消息function SendMsg(msg_text){var contact_id = $('#chat_hander h2').attr("contact_id");  //获取发送给谁消息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};//向后端发送数据$.post("{% url 'send_msg' %}" ,{'data':JSON.stringify(msg_dic),'csrfmiddlewaretoken':GetCsrfToken()},function(callback){console.log(callback);});//向发送数据结束//解释:// $.post 或者 $.get 是调用ajax方法//("URL路径" ,{'data':JSON.stringify(msg_dic)},function(callback){})//
            // 这个第一个参数为指定的ULR 第二个参数为发送的内容 第3个参数为回调函数和返回的值!!
}//发送消息结束

那有没有一劳永逸的方式呢:

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);}}
});

还有一个插件,他实现了“一劳永逸的上半部分,下半部分还是得需要写:JavaScript Cookie library ” ,其实也不是很多自己写的就可以了。

WEBQQ消息存储方式

首先要知道如下几点:C1发给C2消息,消息被发送到服务端之后,当服务端请求过来之后C2接收到消息之后消息就服务端的数据就没有意义了。所以不能使用Mysql、这样的数据置于Redis和Memcache也是没有必要的,当然排除支持数据夸不同设备可以把数据持久化!

那咱们怎么做呢?想象一下数据被C2接收走之后,server端的数据就没有意义了,用消息队列方式是不是更好一点呢?

定义一个队列,队列不能写在接收函数哪里,写个全局的队列即可,并且不能创建一个队列,而是为每个用户创建一个队列。

import Queue
GLOBAL_MQ = {}def new_msg(request):if request.method == 'POST':print request.POST.get('data')#获取用户发过来的数据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()#把消息循环加入到列表中并发送for i in range(stored_msg_nums):msg_lists.append(GLOBAL_MQ[request_user].get())return HttpResponse(json.dumps(msg_lists))

使用Queue&JS实现长轮询

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

#因为队列里目前存的是字符串所以我们需要先给他转换为字符串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就会阻塞,等待有新消息之后会继续往下走,这里如果阻塞到这里了,等有新消息过来之后,把消息加入到msg_lists中后,for循环还是不执行的因为,这个stored_msg_mums是在上面生成的变量下面for调用这个变量的时候他还是为0等返回之后再取得时候,现在stored_msg_nums不是0了,就执行执行for循环了,然后发送数据'''#把消息循环加入到列表中并发送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:#创建一个新队列给这个用户GLOBAL_MQ[str(request.user.userprofile.id)] = Queue.Queue()return HttpResponse(json.dumps(msg_lists))

但是为什么不等待不超时呢?反倒重复的进行连接呢?我服务端不是已经给他阻塞了吗?

这个上面的问题就涉及到Client段的JS的:

 //循环接收消息var RefreshNewMsgs = setInterval(function(){//接收消息
                GetNewMsgs();},3000);

你每一次的的请求,都是一个新的线程,当这个循环结束后自动释放但是,链接发到服务端就被阻塞了,过了一会setInterval又有一个新的连接向服务端,所以服务端每次阻塞的都是一个新的线程,就没有实现咱们想要的效果!

setInterval每一次都新起一个线程!!!

那怎么解决这个问题呢?自己调自己实现一个递归!

看代码:

//接收消息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是谁发过来的,如果是当前打开的ID和from相同说明,我现在正在和他聊天直接显示即可if(msg_item.from == current_open_session_id){AddRecvMsgToChatBox(msg_item)}//判断挡墙打开ID接收
                })})}//接收消息结束

GetNewMsgs是不是一个AJAX啊!他请求完之后会执行一个回调函数啊! 这个回调函数执行的时候是不是代表这个请求结束了?在请求结束执行这个回调函数的时候我在执行以下GetNewMsgs()不就行了,又发起一个请求?

//接收消息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是谁发过来的,如果是当前打开的ID和from相同说明,我现在正在和他聊天直接显示即可if(msg_item.from == current_open_session_id){AddRecvMsgToChatBox(msg_item)}//判断挡墙打开ID接收});//结束循环console.log('run.....agin.....');GetNewMsgs();  })}//接收消息结束

然后把他加载到页面加载完后自动执行中:

  //循环接收消息GetNewMsgs();

Views函数也需要重新写下:(因为队列里如果没有数据,设置为timeout的话就会抛异常,所以我们的抓异常~~)

代码如下:

def new_msg(request):if request.method == 'POST':print request.POST.get('data')#获取用户发过来的数据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就会阻塞,等待有新消息之后会继续往下走,这里如果阻塞到这里了,等有新消息过来之后,把消息加入到msg_lists中后,for循环还是不执行的因为,这个stored_msg_mums是在上面生成的变量下面for调用这个变量的时候他还是为0等返回之后再取得时候,现在stored_msg_nums不是0了,就执行执行for循环了,然后发送数据'''except Exception as e:print ('error:',e)print "\033[43;1等待已超时......15秒.....\033[0m"# 把消息循环加入到列表中并发送for i in range(stored_msg_nums):msg_lists.append(GLOBAL_MQ[request_user].get())else:#创建一个新队列给这个用户GLOBAL_MQ[str(request.user.userprofile.id)] = Queue.Queue()return HttpResponse(json.dumps(msg_lists))

漂亮问题解决:

消息实时效果实现,NICE

这个在python中,如果这么递归,最多1000层,他的等前面的函数执行完后退出!看下面的结果这个CallMyself(n+1)递归下面的print是永远不执行的。

#!/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中它不是这样的,你会发现这个print还会执行,说面函数执行完了。

页面中的聊天框内容,切换聊天人后聊天信息的存储

有这么一种情况,现在我和ALEX聊天,我切换到和武Sir聊天了,但是窗口的内容还在怎么办?如下图:

怎么做呢?多层?如果200个人呢?

怎么做呢?

可以这样,我在和Alex聊天的时候,切换到武Sir之后,把和Alex老师聊天内容保存起来,当和武Sir结束聊天后,在返回来和Alex老师聊天的时候在把Alex老师内容展现,把和武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);};//加载新窗口结束

上述原理:把没被当前用户打开的消息界面时,当有好友发消息来了,就会先把内容保存到字典里,也就是存到内存里先,到打开该好友时再html显示。

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

转载于:https://www.cnblogs.com/fengzaoye/p/5900647.html

django-web聊天相关推荐

  1. Django项目--web聊天室

    需求 做一个web聊天室,主要练习前端ajax与后台的交互: 一对一聊天和群组聊天 添加用户为好友 搜索并添加群组 管理员可以审批用户加群请求,群管理员可以有多个,群管理员可以删除,添加禁言群友 与聊 ...

  2. 基于阿里云用C/C++做了一个http协议与TCP协议的web聊天室的服务器——《干饭聊天室》

    基于阿里云用C/C++做了一个http协议与TCP协议的web聊天室的服务器--<干饭聊天室> 在这里首先感谢前端小伙伴飞鸟 前端技术请看一款基于React.C++,使用TCP/HTTP协 ...

  3. python电商项目源码_Python Django(WEB电商项目构建)

    (坚持每一天,就是成功) Python Django Web框架,Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式,即模型M,模板T和视图V组成. 安装Pyth ...

  4. 使用FastHttpApi构建多人Web聊天室

    为什么80%的码农都做不了架构师?>>>    一般在dotnet core下构建使用web服务应用都使用asp.net core,但通过FastHttpApi组建也可以方便地构建w ...

  5. 基于.NET SingalR,LayIM2.0实现的web聊天室

    LayIM官网 http://www.layui.com/doc/layim.html 博客教程:http://www.cnblogs.com/panzi/p/5767095.html 项目说明:基于 ...

  6. 利用html 5 websocket做个山寨版web聊天室(手写C#服务器)

    在之前的博客中提到过看到html5 的websocket后很感兴趣,终于可以摆脱长轮询(websocket之前的实现方式可以看看Developer Works上的一篇文章,有简单提到,同时也说了web ...

  7. WebSocket请求过程分析及实现Web聊天室

    WebSocket协议是基于TCP的一种新的协议.WebSocket最初在HTML5规范中被引用为TCP连接,作为基于TCP的套接字API的占位符.它实现了浏览器与服务器全双工(full-duplex ...

  8. Django Web 开发极简实战

    课程介绍 本课程是一个系列基础教程,目标是带领读者上手实战 Django Web 开发,课程以 Django 1.10 为基础,通过一个在线视频网站的构建,实战化的介绍 Django Web 开发中涉 ...

  9. ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(一) 之 基层数据搭建,让数据活起来(数据获取)...

    大家好,本篇是接上一篇 ASP.NET SignalR 与 LayIM2.0 配合轻松实现Web聊天室(零) 前言  ASP.NET SignalR WebIM系列第二篇.本篇会带领大家将 LayIM ...

  10. Django Web框架教学笔记-1

    <Django Web框架教学笔记> 目录 文章目录 <Django Web框架教学笔记> 目录 Django框架的介绍 Django的安装 Django框架开发 创建项目的指 ...

最新文章

  1. ASP .NET Core Web Razor Pages系列教程五:更新Razor Pages页面
  2. 云计算管理三利器:Nagios、Ganglia和Splunk
  3. Go语言的分词器(sego)
  4. 2020年阴阳师服务器维护,2020阴阳师2月19日更新官方公告及内容汇总
  5. 如何在uniapp中引入阿里字体图标
  6. java容器类1:Collection,List,ArrayList,LinkedList深入解读
  7. 学数答题160908-数论不等式
  8. english grammar in use 电子版_③ 采购笔记之节点与凭证、采购的进阶、采购合同要素。(电子版整理中)...
  9. mysql数据库优化总结 有图 有用
  10. 组态王怎么做进度条_3分钟学会超实用||进度条amp;游戏血条的制作方法!
  11. html怎么将背景图片旋转,css怎么旋转图片?
  12. python模拟登陆豆瓣_模拟登陆豆瓣并爬取个人主页(爬虫项目三)
  13. 理解景深(Depth of Field)
  14. 联想笔记本计算机在哪里找不到,Win10 Lenovo笔记本电脑显示找不到相机
  15. 【报告分享】潮流涌动下的追潮众生与消费洞察-阿里妈妈.pdf(附下载链接)...
  16. 如何format格式化ftl模板
  17. Python编程基础题(26-家务分配问题)
  18. 马云厌恶美国 阿里巴巴转道香港上市
  19. 论文查重的要求是什么 查重率不能高于多少
  20. 区块链珠宝溯源,解决珠宝行业信任危机

热门文章

  1. 微软确定 Win10 付费才能玩 还能不能愉快玩耍?
  2. Android MVP模式的初识
  3. [Unity3d]u3d中定时器的使用
  4. 创建Server 2012 VHDX虚拟磁盘模板
  5. DropDownList的用法
  6. SQL Server 性能调优(方法论)
  7. Tensorflow 2.0的新特性
  8. OpenCV获取图像某点的颜色值,并设置某点的颜色
  9. 【08月14日】A股ROE最高排名
  10. SDNU 1330.Max Sum(最大子序列和)