websocket的应用---Django
websocket的应用---Django
1.长轮询
轮询:在前端通过写js实现。缺点:有延迟、服务器压力大。
就是客户端通过一定的时间间隔以频繁请求的方式向服务器发送请求,来保持客户端和服务器端的数据同步。问题很明显,当客户端以固定频率向服务器端发送请求时,服务器端的数据可能并没有更新,带来很多无谓请求,浪费带宽,效率低下。
长轮询
首先需要为每个用户维护一个队列,用户浏览器会通过js递归向后端自己的队列获取数据,自己队列没有数据,会将请求夯住(去队列中获取数据),夯一段时间之后再返回。
注意:一旦有数据立即获取,获取到数据之后会再发送请求。
用户发来请求之后,最多会夯住N秒(30s),因为有消息的时候回立即返回,没有消息时才最多夯N秒。
2.websocket
2.1 原理
- WebSocket 是 HTML5 一种新的协议。它实现了浏览器与服务器全双工通信,能更好的节省服务器资源和带宽并达到实时通讯,它建立在 TCP 之上,同 HTTP 一样通过 TCP 来传输数据,但是它和 HTTP 最大不同是:
- WebSocket 是一种双向通信协议,在建立连接后,WebSocket 服务器和 Browser/Client Agent 都能主动的向对方发送或接收数据,就像 Socket 一样;
- WebSocket 需要类似 TCP 的客户端和服务器端通过握手连接,连接成功后才能相互通信。
2.2具体实现:
客户端向服务端发送随机字符串,在http的请求头中
Sec-WebSocket-Key服务端接受到到随机字符串
- 随机字符串 + 魔法字符串
- sha1加密
- base64加密
- 放在响应头中给用户返回
Sec-WebSocket-Accept
客户端浏览器会进行校验,校验不通过:服务端不支持websocket协议。
客户端和服务端进行相互收发消息,数据加密和解密。
解密细节:
获取第二个字节的后7位,payload len
判断payload len的值
= 127
2字节 + 8字节 + 4字节 + 数据
= 126
2字节 + 2字节 + 4字节 + 数据
<= 125
2字节 + 4字节 + 数据
使用masking key 对数据进行解密
2.3 手动创建支持websocket的服务端(理解)
服务端
import socket
import hashlib
import base64 def get_headers(data):
"""
将请求头格式化成字典
:param data:
:return:
"""
header_dict = {}
data = str(data, encoding='utf-8')
header, body = data.split('\r\n\r\n', 1)
header_list = header.split('\r\n')
for i in range(0, len(header_list)):
if i == 0:
if len(header_list[i].split(' ')) == 3:
header_dict['method'], header_dict['url'], header_dict['protocol'] = header_list[i].split(' ')
else:
k, v = header_list[i].split(':', 1)
header_dict[k] = v.strip()
return header_dict def get_data(info):
"""
对数据进行就解密
"""
payload_len = info[1] & 127
if payload_len == 126:
extend_payload_len = info[2:4]
mask = info[4:8]
decoded = info[8:]
elif payload_len == 127:
extend_payload_len = info[2:10]
mask = info[10:14]
decoded = info[14:]
else:
extend_payload_len = None
mask = info[2:6]
decoded = info[6:] bytes_list = bytearray()
for i in range(len(decoded)):
chunk = decoded[i] ^ mask[i % 4]
bytes_list.append(chunk)
body = str(bytes_list, encoding='utf-8')
return body sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind(('127.0.0.1', 8002))
sock.listen(5) # 等待用户连接
conn, address = sock.accept()
# 握手环节
header_dict = get_headers(conn.recv(1024))
# 公认的魔法字符串(固定)
magic_string = '258EAFA5-E914-47DA-95CA-C5AB0DC85B11' random_string = header_dict['Sec-WebSocket-Key']
value = random_string + magic_string
ac = base64.b64encode(hashlib.sha1(value.encode('utf-8')).digest())
# 给前端发送的响应数据
response = "HTTP/1.1 101 Switching Protocols\r\n" \
"Upgrade:websocket\r\n" \
"Connection: Upgrade\r\n" \
"Sec-WebSocket-Accept: %s\r\n" \
"WebSocket-Location: ws://127.0.0.1:8002\r\n\r\n" response = response %ac.decode('utf-8') conn.send(response.encode('utf-8')) # 接受数据
while True:
data = conn.recv(1024)
msg = get_data(data)
print(msg)
html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<input type="button" value="开始" onclick="startConnect();"> <script>
var ws = null;
function startConnect() {
// 1. 内部会先发送随机字符串
// 2. 内部会校验加密字符串
ws = new WebSocket('ws://127.0.0.1:8002')
}
</script>
</body>
</html>
2.4 企业应用
我们学过django和flask框架,内部基于wsgi做的socket,默认他俩都不支持websocket协议,只支持http协议。
flask中应用
pip3 install gevent-websocket
django中应用
pip3 install channels
请保留2个案例(django):
在基于Django实现的时候因为需要导入channels模块,会跟Django中的wsgiref发生冲突所以需要在配置文件中添加配置:
1.首先是在settings文件中的需要注册这个应用:
INSTALLED_APPS = ['channels']
2.在settings文件中增加一个文件的路径:
ASGI_APPLICATION = "wechat.routing.application"
这个文件是用来写前端使用websocket发送数据时指定的接收路径,相当于url;
1v1示例
接收数据的url
from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from app01 import consumers application = ProtocolTypeRouter({
'websocket': URLRouter([
url(r'^x1/$', consumers.ChatConsumer),
])
})
内部处理的数据的代码:
from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumer class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
""" websocket连接到来时,自动执行 """
print('有人来了')
self.accept() def websocket_receive(self, message):
""" websocket浏览器给发消息时,自动触发此方法 """
print('接收到消息', message) self.send(text_data='收到了') # self.close() def websocket_disconnect(self, message):
print('客户端主动断开连接了')
raise StopConsumer()
前端操作的代码:
<script>
# 指定一个变量
var ws; # 当加载页面时自动执行
$(function () {
# 执行函数
initWebSocket();
}); function initWebSocket() {
# 与后端的websocket建立连接,创建一个对象
ws = new WebSocket("ws://127.0.0.1:8000/x1/"); # 成功得到时候自动触发这个方法
ws.onopen = function(){
$('#tips').text('连接成功');
}; # 接收到消息的时候自动触发这个方法;
ws.onmessage = function (arg) {
var tag = document.createElement('div');
tag.innerHTML = arg.data;
$('#content').append(tag);
}; # 服务端断开连接客户端也断开
ws.onclose = function () {
ws.close();
}
}
# 向服务端发送数据,使用创建的ws对象.send方就可以发送数据
function sendMessage() {
ws.send($('#txt').val());
}
</script>
n v n 示例
- 在routing文件中的代码:前端websocket发送数据的路径
from channels.routing import ProtocolTypeRouter, URLRouter
from django.conf.urls import url
from app01 import consumers
application = ProtocolTypeRouter({
'websocket': URLRouter([
url(r'^x1/$', consumers.ChatConsumer),
])
})
# 这个文件写的是和前端websocket创建连接的路径,
在consumers文件中的代码:websocket的模块应用
这里主要是接收数据对数据进行一个处理
from channels.exceptions import StopConsumer
from channels.generic.websocket import WebsocketConsumer
from asgiref.sync import async_to_sync
class ChatConsumer(WebsocketConsumer):
def websocket_connect(self, message):
""" websocket连接到来时,自动执行 """
print('有人来了')
# 里边的'22922192'字符串是随意指定的,相当于一个标识;
async_to_sync(self.channel_layer.group_add)('22922192', self.channel_name)
self.accept()
def websocket_receive(self, message):
""" websocket浏览器给发消息时,自动触发此方法 """
print('接收到消息', message)
# 接收到消息 {'type': 'websocket.receive', 'text': 'fdsfsdfafd'}
async_to_sync(self.channel_layer.group_send)('22922192', {
# 这个是指定函数执行
'type': 'xxx.ooo',
# 将获取到的消息
'message': message['text']
})
def xxx_ooo(self, event):
# 触发这个方法的时候就将消息发出
message = event['message']
self.send(message)
def websocket_disconnect(self, message):
""" 断开连接 """
print('客户端主动断开连接了')
async_to_sync(self.channel_layer.group_discard)('22922192', self.channel_name)
raise StopConsumer()
另一种websocket的写法
class NewChatConsumer(WebsocketConsumer):
def connect(self):
print('有人来了')
async_to_sync(self.channel_layer.group_add)('22922192', self.channel_name)
self.accept()
def receive(self, text_data=None, bytes_data=None):
print('接收到消息', text_data)
async_to_sync(self.channel_layer.group_send)('22922192', {
'type': 'xxx.ooo',
'message': text_data
})
def xxx_ooo(self, event):
message = event['message']
self.send(message)
def disconnect(self, code):
print('客户端主动断开连接了')
async_to_sync(self.channel_layer.group_discard)('22922192', self.channel_name)
其实都是一样的,只是这个源码内部是多了几步的调用,而这种写法是将调用的步骤省去;
- 前端websocket的写法:
<script>
# 指定一个变量
var ws;
# 当加载页面时自动执行
$(function () {
# 执行函数
initWebSocket();
});
function initWebSocket() {
# 与后端的websocket建立连接,创建一个对象
ws = new WebSocket("ws://127.0.0.1:8000/x1/");
# 成功得到时候自动触发这个方法
ws.onopen = function(){
$('#tips').text('连接成功');
};
# 接收到消息的时候自动触发这个方法;
ws.onmessage = function (arg) {
var tag = document.createElement('div');
tag.innerHTML = arg.data;
$('#content').append(tag);
};
# 服务端断开连接客户端也断开
ws.onclose = function () {
ws.close();
}
}
# 向服务端发送数据,使用创建的ws对象.send方就可以发送数据
function sendMessage() {
ws.send($('#txt').val());
}
</script>
https://www.cnblogs.com/zhufanyu/p/11951654.html
websocket的应用---Django的更多相关文章
- vue+django实现websocket连接
一.概述 在项目中,需要使用websocket,来展示一些实时信息. 这里使用django 3.1.5 二.django项目 安装模块 pip3 install django-cors-headers ...
- django面试题
1. 对Django的认识? #1.Django是走大而全的方向,它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构.以及全功能的管理后台. #2.D ...
- Django 的认识,面试题
Django 的认识,面试题 1. 对Django的认识? #1.Django是走大而全的方向,它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构.以及全 ...
- django面试八
1. 对Django的认识? #1.Django是走大而全的方向,它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构.以及全功能的管理后台. #2.Dja ...
- Django 的认识,题型
Django 的认识,面试题 链接:https://www.cnblogs.com/chongdongxiaoyu/p/9403399.html 1. 对Django的认识? #1.Django是走大 ...
- 【翻译】Django Channels 官方文档 -- Tutorial
Django Channels 官方文档 https://channels.readthedocs.io/en/latest/index.html 前言: 最近课程设计需要用到 WebSocket,而 ...
- Django实现WebSSH操作物理机或虚拟机
我想用它替换掉xshell.crt之类的工具 WebSSH操作物理机或虚拟机 Django实现WebSSH操作Kubernetes Pod文章发布后,有小伙伴说咖啡哥,我们现在还没有用上Kuberne ...
- Django常用知识整理
Django 的认识,面试题 1. 对Django的认识? #1.Django是走大而全的方向,它最出名的是其全自动化的管理后台:只需要使用起ORM,做简单的对象定义,它就能自动生成数据库结构.以及全 ...
- django考点
django考点 1 列举Http请求中常见的请求方式2 谈谈你对HTTP协议的认识.1.1 长连接3 简述MVC模式和MVT模式4 简述Django请求生命周期5 简述什么是FBV和CBV6 谈一谈 ...
随机推荐
- Django入门实战一
前言 Django是高水准的Python编程语言驱动的一个开源模型.视图,控制器风格的Web应用程序框架,它起源于开源社区.使用这种架构,程序员可以方便.快捷地创建高品质.易维护.数据库驱动的应用程序 ...
- draggable()拖拽时限制移动区域
jQuery-UI为我们提供了一个非常便捷的拖拽方法:draggable(),在使用此方法时,我们可能会希望控件只在某一区域中移动,不能被拖出边界,这样的话我们可以使用下面的方法: 调用draggab ...
- WIN7远程桌面连接提示:“发生身份验证错误。要求的函数不受支持”
问题 WIN7远程桌面连接–"发生身份验证错误.要求的函数不受支持" 最近WIN7升级补丁后发现远程桌面无法连接了,报"发生身份验证错误.要求的函数不受支持"的 ...
- Spring Cloud 各个组件角色简介
概述 SpringCloud 是一个全家桶式的技术栈,包含了很多组件:包含 Eureka.Ribbon.Feign.Zuul .Hystrix等.每个组件完成对应的功能 组件介绍 - 服务发现 Eur ...
- OpenResty 简介
OpenResty 简介 OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台.我们知道开发 Nginx 的模块需要用 C 语言,同时还要熟悉它的源码,成本和门槛比较高.国人 ...
- lua脚本简介
Lua [1] 是一个小巧的脚本语言.它是巴西里约热内卢天主教大学(Pontifical Catholic University of Rio de Janeiro)里的一个由Roberto Ier ...
- JVM——GC(垃圾回收)算法
一.垃圾回收的基本概念 垃圾回收(GC,Garbage Collection),指内存中不会再被使用的对象清理掉. 垃圾回收有很多种算法:如引用计数法.标记压缩法.复制算法.分代/分区的思想 二.垃圾 ...
- python安装库报错的处理方法
在安装python map库时遇到了还多问题,找了好的方法都没有安装成功,最后改安装basemap库参考了了:https://www.jb51.net/article/147780.htm一文操作,最 ...
- 手摸手带你用Hexo撸博客(三)之添加评论系统
原文地址 注: 笔者采用的是butterfly主题, 主题内置集成评论系统 butterfly主题开启评论 开启评论需要在comments-use中填写你需要的评论. 以Valine为例 commen ...
- java中string、stringBuild、stringBuffer的区别
(1)string 1,Stirng是对象不是基本数据类型 2,String是final类,不能被继承.是不可变对象,一旦创建,就不能修改它的值. 3,对于已经存在的Sti ...