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 谈一谈 ...
随机推荐
- 我们为什么选择VUE来构建前端
很多使用过VUE的程序员,对VUE的评价是"Vue.js 兼具angular.js和react.js的优点,并剔除了它们的缺点". 那么,他真的值得这么高的评价嘛? Vue.js的 ...
- mysql数据安全之利用二进制日志mysqlbinlog备份数据
mysql数据安全之利用二进制日志mysqlbinlog备份数据 简介:如何利用二进制日志来备份数据 什么是二进制日志: 二进制日志就是记录着mysql数据库中的一些写入性操作,比如一些增删改,但是, ...
- easyui中权限分配和添加 前后端代码
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding= ...
- java 反射给字段重新赋值
1.获取实体的所有字段,遍历 2.获取字段类型 3.调用字段的get方法,判断字段值是否为空 4.如果字段值为空,调用字段的set方法,为字段赋值 Field[] field = model.getC ...
- 使用Arduino点亮ESP-01S,ESP8266-01S上的板载LED
因为在开发ESP-01s远程控制中觉得接线麻烦,又因为ESP-01s板子上带有LED灯,那就先点亮板载LED, 如图所示: 打开Arduino 把代码copy进去,再编译烧录,就可以看见LED灯每隔 ...
- 在mac上使用vscode创建第一个C++项目
https://blog.csdn.net/bujidexinq/article/details/106539523 准备工作:安装好vscode安装插件『C/C++』正式开始:首先是创建一个空的文件 ...
- Socket粘包问题的3种解决方案,最后一种最完美!
在 Java 语言中,传统的 Socket 编程分为两种实现方式,这两种实现方式也对应着两种不同的传输层协议:TCP 协议和 UDP 协议,但作为互联网中最常用的传输层协议 TCP,在使用时却会导致粘 ...
- 一键配置网卡IP(win10)脚本
前两天有个小伙伴问我,如何快速配置IP,在公司在家里在宿舍,快速配置IP,然后我特别为这个小伙伴写了一个脚本. @echo off mode con: cols=60 lines=25 title 网 ...
- PHP 获取天气
/** * 获取天气 */ function get_tq () { //获取用户ip地址 $ip = get_real_ip(); // $ip = '123.125.71.38'; //根据ip地 ...
- 晋升新一线的合肥,跨平台的.NET氛围究竟如何?
大伙可能不知道,2020年合肥已经成功晋升为新一线城市了.本文通过对目前合肥.NET招聘信息以及公众号的相关数据的分析来看下目前合肥.NET的大环境.就着2020中国.NET开发者峰会的顺利举行的东风 ...