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 谈一谈 ...
随机推荐
- Web服务器-并发服务器-多进程(3.4.1)
@ 目录 1.优化分析 2.代码 3. 关于作者 1.优化分析 在单进程的时候,相当于 是来一个客户,派一个人去服务一下 效率低,现在使用多进程来服务 假设场景 100个人同时访问页面 单进程:一次处 ...
- css做keylogger
下载keylogger:https://github.com/maxchehab/css-keylogging 参考讲解:https://blog.csdn.net/weixin_34138139/a ...
- JDK8新特性详解(一)
虽然JDK8已经出来了N久,其新特性也在日益改变着我们的编码习惯和风格.虽然有些新特性用起来很顺手,但是总是傻傻分不清到底是哪个版本的.趁今天有时间,我们就来总结一下,JDK8有哪些能提升我们开发效率 ...
- 【Azure Service Bus】 Service Bus如何确保消息发送成功,发送端是否有Ack机制
问题描述 Service Bus如何确保消息发送成功,发送端是否有Ack机制(是否有回调API告诉发送端,服务端已经收到消息)?根据对.NET发送Service Bus消息代码的分析,发送方法queu ...
- Python之复制列表
将一个列表的数据复制到另外一个列表中. 1 a = [1,2,3] #定义列表a 2 3 b = a[:] #将列表a的切片赋值给b,也可以理解为将b的值设置为a[:] 4 5 print(a) #打 ...
- NuGet 学习笔记(1)--Nuget安装使用
安装NuGet扩展 要使用NuGet首先需要安装它(vs2013NuGet) 1. 点击 工具(Tools)-->扩展管理器(Extensions and Updates)...-->右上 ...
- ArrayList哪种循环效率更好你真的清楚吗
ArrayList简介 声明:以下内容都是基于jdk1.8的 ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了 ...
- 基于snort、barnyard2和base的 网络入侵检测系统的部署与应用
1.项目分析 1.1.项目背景 伴随着互联网产业的不迅猛发展,新兴技术层数不穷,互联网通讯技术逐渐成为了各行各业不可替代的基础设施,越来越多的业务都是依靠互联网来得以实现.随着我国科技产业的飞速发展, ...
- jupyter安装插件Nbextensions,实现代码提示功能(终极方法)
jupyter安装插件,实现代码提示功能 第一步 pip install jupyter_contrib_nbextensions -i https://mirrors.tuna.tsinghua.e ...
- 短信平台软件开发,短信发送平台销售,短信软件源码,G客短信发送平台
一:web短信平台组成 需要短信软件平台源码的联系QQ:290615413 vx:290615413 整套短信系统平台还是由B/S(客户端+后台,取消了以前C/S的管理后台) ,C/S发送服务端和 ...