市住房城乡建设网站,企业型网站价目表,中国建设银行官网站额度申请,商标图案自动生成1、websocket 相关
实现一个系统#xff0c;20 个用户同时打开网站#xff0c;呈现出来一个群聊界面
解决方案 轮询#xff1a;让浏览器每隔2s向后台发送一次请求#xff0c;缺点#xff1a;延迟#xff0c;请求太多网站压力大 长轮询#xff1a;客户端向服务端发送请…1、websocket 相关
实现一个系统20 个用户同时打开网站呈现出来一个群聊界面
解决方案 轮询让浏览器每隔2s向后台发送一次请求缺点延迟请求太多网站压力大 长轮询客户端向服务端发送请求服务器最多宕20s一旦有数据接入就立即返回。数据的响应没有延迟时间。 websocket客户端和服务端创建连接后不断开实现双向通信 轮询 访问 /home/ 显示的聊天室界面 点击发送内容数据可以发送到后台 定时获取消息发送到前端
长轮询 访问/home/ 显示聊天界面 → 每个用户创建一个队列 点击发送内容数据也可以发送到后台 → 扔到每个用户的队列中 递归获取消息 去自己的队列中获取数据然后展示在界面中。
问题 服务端持有连接压力是否会很大 如果基于IO多复用 异步还会有这种情况吗 可以 如果后台有100线程同时对应100个用户的请求则在等待期间假设10s这100个用户则一直占用县城如果有更多的用户请求则需等待释放。
webSocket
原来的web中 http协议 无状态 短连接 客户端主动连接服务端。 客户端向服务端发送消息服务端接收后返回数据 客户端接收到数据 断开连接 https协议 http协议 对数据进行加密
我们在开发过程中想要保留一些状态信息基于Cookie来做
现在支持 http协议一次请求一次响应 websocket协议 创建持有的连接不断开基于这个连接可以进行收发数据。【服务端向客户端主动推送消息】 web聊天室 实时图标柱状图、饼图echarts
WebSocket原理 http协议 连接 数据传输 断开连接 websocket 协议 → 建立在 http 协议之上 连接 客户端发出请求 握手验证 客户端发送一段消息后端接收到消息后做一些特殊处理并返回。服务端支持 websocket 协议 https://www.cnblogs.com/wupeiqi/p/6558766.html 客户端向服务端发送握手信息
GET /chatsocket HTTP/1.1
Host: 127.0.0.1:8002
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: http://localhost:63342
Sec-WebSocket-Version: 13
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
...
...服务端接收 接收后的加密过程
// Sec-WebSocket-Key 与 magic String 进行拼接
Sec-WebSocket-Key: mnwFxiOlctXFN/DeMt1Amg
magic_string 258EAFA5-E914-47DA-95CA-C5AB0DC85B11
v1 mnwFxiOlctXFN/DeMt1Amg 258EAFA5-E914-47DA-95CA-C5AB0DC85B11v2 hmac1(v1) // 通过 hmac1 进行加密
v3 base64(v2) // 通过 base64 加密返回数据
HTTP/1.1 101 Switching Protocols
Upgrade:websocket
Connection: Upgrade
Sec-WebSocket-Accept: 密文收发数据加密 先获取第 2 个字节对应 8 位 在获取第二个字节的后 7 位 断开连接
Django 框架实现 WebSocket
Django 默认不支持WebSocket安装第三方组件 pip install channels 版本不能超过4.0最好是3.0.5不然不能成功启动asgi
配置
django channels - 武沛齐 - 博客园
注册 channels INSTALLED_APPS [django.contrib.admin,django.contrib.auth,django.contrib.contenttypes,django.contrib.sessions,django.contrib.messages,django.contrib.staticfiles,channels,app01.apps.App01Config]在 settings.py 中添加 asgi_application ASGI_APPLICATION ws_demo.asgi.application修改 asgi.py文件 import osfrom django.core.asgi import get_asgi_applicationfrom channels.routing import ProtocolTypeRouter, URLRouterfrom ws_demo import routingsos.environ.setdefault(DJANGO_SETTINGS_MODULE, ws_demo.settings)# application get_asgi_application()# 支持 http 和 WebSocket 请求application ProtocolTypeRouter({http: get_asgi_application(), # 自动找 urls.py 找视图函数 --》 httpwebsocket: URLRouter(routings.websocket_urlpatterns), # routing(urls)、 consumers(views)})在 settings.py同级目录下创建routings.py
from django.urls import re_pathfrom app01 import consumerswebsocket_urlpatterns [# 示例 url xxxxx/room/x1/re_path(rroom/(?Pgroup\w)/$, consumers.ChatConsumer.as_asgi())
]在 app01 目录下创建consumers.py 用于设置 WebSocket 请求
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumerclass ChatConsumer(WebsocketConsumer): # 继承WebsocketConsumerdef websocket_connect(self, message):print(有人进行连接了。。。。)# 有客户端向后端发送 WebSocket 连接的请求时自动触发(握手)self.accept()def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据自动触发接收消息print(message)self.send(不要回复不要回复)def websocket_disconnect(self, message):# 客户端向服务端断开连接时自动触发print(连接断开)raise StopConsumer()django 中需要了解
wsg asgi wsgi 异步 WebSocket 聊天室 访问地址看到聊天室界面使用 http 请求 让客户端主动向服务端发起 websocket连接服务端接收到连接后进行握手 客户端向后台发布WebSocket请求 var socket new WebSocket(ws://localhost:8000/room/123/)服务端接收消息 from channels.generic.websocket import WebsocketConsumerfrom channels.exceptions import StopConsumerclass ChatConsumer(WebsocketConsumer): # 继承WebsocketConsumerdef websocket_connect(self, message):print(有人进行连接了。。。。)# 有客户端向后端发送 WebSocket 连接的请求时自动触发(握手)self.accept()收发消息客户端向服务端发消息 客户端发送消息
!DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/titlestyle.message {height: 300px;border: 1px solid #ddd;width: 100%;}/style
/head
body
div classmessage idmessage/div
divlabelinput typetext placeholder请输入 idtxt/labelinput typebutton value发送 οnclicksendMessage()
/div
script// 创建websocket对象向后台发送请求let socket new WebSocket(ws://localhost:8000/room/123/);function sendMessage(){let tag document.getElementById(txt);socket.send(tag.value);}/script
/body
/html服务端接收消息
def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据自动触发接收消息text message[text]print(接收到的消息为:, text)收发消息服务端主动发给客户端
# 连接之后服务端给客户端发送消息
self.send(来了啊 )// 回调函数客户端接收服务端消息
socket.onmessage function (event){console.log(event.data)
}其他方法
// 创建websocket对象向后台发送请求
let socket new WebSocket(ws://localhost:8000/room/123/);// 当客户端和服务端刚创建好连接(self.accept)之后自动触发.
socket.onopen function (event){let tag document.createElement(div);tag.innerText [连接成功];document.getElementById(message).appendChild(tag);
}// 回调函数客户端接收服务端消息
socket.onmessage function (event){let tag document.createElement(div);tag.innerText event.data;document.getElementById(message).appendChild(tag);
}// 当断开连接时触发该函数
socket.onclose function (event){let tag document.createElement(div);tag.innerText [连接断开];document.getElementById(message).appendChild(tag);
}function sendMessage(){let tag document.getElementById(txt);socket.send(tag.value);
}function closeMessage(){socket.close();
}function handleKeyPress(event){if (event.keyCode 13){document.getElementById(send).click();document.getElementById(txt).value ;}
}document.onkeydown handleKeyPress;def websocket_connect(self, message):print(有人进行连接了。。。。)# 有客户端向后端发送 WebSocket 连接的请求时自动触发(握手)self.accept()# 连接之后服务端给客户端发送消息self.send(来了啊 )def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据自动触发接收消息text message[text]print(接收到的消息为:, text)# 当接收的值为【关闭】则服务端关闭连接if text close:self.close()returnelse:self.send(text NB)def websocket_disconnect(self, message):# 客户端向服务端断开连接时自动触发print(断开连接)# 当客户端断开连接时服务端也需关闭与客户端的连接连接是双向的raise StopConsumer()小结
现在的交互还是停留在对某个人的操作
群聊
基于 channels 中提供的channel layers 来实现
settings 中配置
# 声明基于内存的 channel layers
CHANNEL_LAYERS {default: {BACKEND: channels.layers.InMemoryChannelLayer}
}也可声明基于 redis 的 channel layer → pip install channels-redis # 基于redis 内存的 channel layers
CHANNEL_LAYERS {default: {BACKEND: channels_redis.core.RedisChannelLayer,CONFIG: {hosts: [redis://10.211.55.25:6379/1]}}
}consumers 中特殊的代码
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_syncclass ChatConsumer(WebsocketConsumer): # 继承WebsocketConsumerdef websocket_connect(self, message):# 接收客户端的连接self.accept()print(连接成功)# 获取群号group_num self.scope[url_route][kwargs].get(group)# 将这个客户端的链接对象添加到某个地方内存或者 redis# self.channel_layer.group_add(group_num, self.channel_name)async_to_sync(self.channel_layer.group_add)(group_num, self.channel_name)def websocket_receive(self, message):# 浏览器基于 WebSocket 向后端发送数据自动触发接收消息text message[text]print(接收到的消息为:, text)group_num self.scope[url_route][kwargs].get(group)print(group_num, group_num)# 通知组内的所有的客户端执行 xx_oo方法在方法中可以自定义任意的功能# self.channel_layer.group_send(group_num, {type: xx.oo, message: message})async_to_sync(self.channel_layer.group_send)(group_num, {type: xx.oo, message: message})def xx_oo(self, event):text event[message][text]print(发送的 text, text)self.send(text) # 给组中的每一个人去发送消息def websocket_disconnect(self, message):# 客户端向服务端断开连接时自动触发print(断开连接)group_num self.scope[url_route][kwargs].get(group_num)# self.channel_layer.group_discard(group_num, self.channel_name)async_to_sync(self.channel_layer.group_discard)(group_num, self.channel_name)raise StopConsumer()
!DOCTYPE html
html langen
headmeta charsetUTF-8titleTitle/titlestyle.message {height: 300px;border: 1px solid #ddd;width: 100%;}/style
/head
body
div classmessage idmessage/div
divlabelinput typetext placeholder请输入 idtxt/labelinput typebutton value发送 οnclicksendMessage() idsendinput typebutton value关闭链接 οnclickcloseMessage()
/div
script// 创建websocket对象向后台发送请求let socket new WebSocket(ws://localhost:8000/room/{{ group_num }}/);// 当客户端和服务端刚创建好连接(self.accept)之后自动触发.socket.onopen function (event){let tag document.createElement(div);tag.innerText [连接成功];document.getElementById(message).appendChild(tag);}// 回调函数客户端接收服务端消息socket.onmessage function (event){let tag document.createElement(div);tag.innerText event.data;document.getElementById(message).appendChild(tag);}// 当断开连接时触发该函数socket.onclose function (event){let tag document.createElement(div);tag.innerText [连接断开];document.getElementById(message).appendChild(tag);}function sendMessage(){let tag document.getElementById(txt);socket.send(tag.value);}function closeMessage(){socket.close();}function handleKeyPress(event){if (event.keyCode 13){document.getElementById(send).click();document.getElementById(txt).value ;}}document.onkeydown handleKeyPress;/script
/body
/html总结 WebSocket 是什么 协议 django 中实现 WebSocket channels 组件 单独连接和收发数据 手动创建列表 channel layers
运维运维开发的同学使用 WebSocket 实现代码发布系统项目