0、实现服务端和客户端通信
解决方案
轮训,让浏览器每隔2s向后台发送一次请求。缺点: 延迟、请求太多网站压力大
长轮训,客户端向服务端发送请求,服务端最多夯205,一旦有数据到来就立即返回。数据的响应没有延迟。 (大平台、webQQ、web微信)
websocket,客户端和服务端创建连接不断开,那么就可以先实现双向通信。
一、websocket介绍:
| websocket,web版的 socket。 |
| 原来Web中: |
| 。http协议,无状态&短连接 |
| 客户端主动连接服务端 |
| 。客户端向服务端发送消息,服务端接收到返回数据 |
| 客户端接收到数据。 |
| 断开连接 |
| https一些+对数据进行加密 |
| 我们在开发过程中想要保留一些状态信息,基于Cookie来做 |
| 现在支持: |
| 。 http协议,一次请求一次响应 |
| 。websocket协议,创建连持久的连接不断开,基于这个连接可以进行收发数据。[服务端向客户端主动推送消息]o web聊天室 |
| 。实时图表,柱状图、饼图 (Highcharts) |
| channels 4.0之后默认不带Daphne服务器了。 |
| |
| 解决方案可以有两种: |
| 1.指定channels的版本为3.x; |
| 2.安装时使用pip3 install -U channels[“daphne”] |
http协议
-连接
-数据传输
-断开连接
websocket协议,是建立在http协议之上的。
-连接,客户端发起。0
-握手(验证),客户端发送一个消息,后端接收到消息再做一些特殊处理并返回。服务端支持websocket协议”客户端向服务端发送
| GET /chatsocket HTTP/1.1 |
| Host: 127.0.0.1:8002 |
| Connection: Upgrade |
| Praqma: no-cache |
| Cache-Control: no-cache |
| Upgrade: websocket |
| Origin: http://localhost:63342 |
| Sec-WebSocket-Version: 13 |
| Sec-WebSocket-Kev: mnwFxiolctXFN/DeMt1Amg== |
| Sec-WebSocket-Extensions: permessage-deflate; client max window bits |
| .. |
| ... |
| r/n/r/n |
-服务端接收
| mnwFxiolctXFN/DeMt1Amg== 与 magic string 进行拼接magic string =258EAFA5-E914-47DA-95CA-C5ABODC85B11 |
| |
| vl="mnwFxi0lctXFN/DeMt1Amg==”+"258EAFA5-E914-47DA-95CA-C5ABODC85B11"v2 = hmacl(vl) |
| V3=base64(v2) |
| |
| HTTP/1.1 101 Switching ProtocolsUpgrade:websocketConnection: UpgradeSec-WebSocket-Accept: 密文 |
-收发数据 (加密)
| b"adasdjf;akidfp;iujas;ldkjfpaisudflkasjd;fkjas;dkjf;aksjdf;ajksd;fjka;sdijkf" |
- 先获取第2个字节,8位。10001010
- 再获取第二个字节的后7位0001010 -> payloadlen
- 127,2字节,8个字节,其他字节 (4字节 masking key + 数据)
- 126,2字节,2个字节,其他字节 (4字节 masking key +数据)
- <=125,2字节
- 其他字节 (4字节 masking key + 数据)
- 获取maskingkey,然后对数据进行解密
| var DECODED =''; |
| for (var i = 0; < ENCODED.length; i++) { |
| (DECODED[I] = ENCODED[i]MASK^[i% 4] |
| } |
-断开连接

二、后端
1. 安装(基于django3.x)
| pip3 install channels==3.0.3 -i https://pypi.douban.com/simple |
2. settings配置
a. app注册
| |
| INSTALLED_APPS = [ |
| 'channels', |
| ] |
b. 配置变量
| ASGI_APPLICATION = "qq_chart.asgi.application" |
| ASGI_APPLICATION = '当前项目名同名的文件名.asgi.application' |
3.修改asgi文件(默认不支持websocket,只支持http)
| |
| import os |
| from django.core.asgi import get_asgi_application |
| from channels.routing import ProtocolTypeRouter, URLRouter |
| from . import routings |
| os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'qq_chart.settings') |
| |
| |
| application = ProtocolTypeRouter({ |
| 'http':get_asgi_application(), |
| 'websocket':URLRouter(routings.websocket_urlpatterns) |
| }) |
4. 在settings.py同级目录下创建routings.py 路由
| from django.urls import re_path |
| from app01 import consumers |
| |
| websocket_urlpatterns = [ |
| re_path(r'ws/(?P<group>\w+)/$', consumers.ChatConsumer.as_asgi()) |
| ] |
5.创建websocket视图 consumers.py
| 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): |
| '''客户端请求建立链接时 自动触发''' |
| self.accept() |
| |
| def websocket_receive(self, message): |
| '''客户端发送数据过来 自动触发''' |
| print(message) |
| self.send('小心电信诈骗') |
| |
| def websocket_disconnect(self, message): |
| '''客户端断开链接之后 自动触发''' |
| print('断开链接') |
| raise StopConsumer() |

websocket收发数据
前端
| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <script src="./js/vue.js"></script> |
| <title>Title</title> |
| </head> |
| <style> |
| .message { |
| height: 300px; |
| border: 1px solid #dddddd; |
| width: 100%; |
| } |
| </style> |
| <body> |
| <div class="message" id="message"></div> |
| <div> |
| <input type="text" placeholder="请输入" id="txt"> |
| <input type="button" value="发送" onclick="sendMessage();"> |
| <input type="button" value="close" onclick="closeConn();"> |
| </div> |
| |
| <script> |
| socket = new WebSocket("ws://127.0.0.1:8000/room/123/") |
| |
| |
| socket.onopen = function (event) { |
| let tag = document.createElement("div"); |
| tag.innerText = "[连接成功]" |
| document.getElementById("message").appendChild(tag); |
| } |
| |
| |
| socket.onclose = 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); |
| } |
| |
| |
| |
| function sendMessage() { |
| let tag = document.getElementById('txt'); |
| socket.send(tag.value) |
| } |
| |
| function closeConn() { |
| socket.close(); |
| } |
| |
| |
| |
| </script> |
| </body> |
| </html> |
后端
| 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): |
| '''客户端请求建立链接时 自动触发''' |
| print('有人来连接了') |
| self.accept() |
| |
| self.send('小心电信诈骗') |
| |
| def websocket_receive(self, message): |
| '''客户端发送数据过来 自动触发''' |
| text = message['text'] |
| print("接收到消息---->", text) |
| |
| if text == '关闭': |
| self.close() |
| |
| return |
| res = '{}sb'.format(text) |
| |
| self.send(res) |
| |
| |
| def websocket_disconnect(self, message): |
| '''客户端断开链接之后 自动触发''' |
| print('断开链接') |
| raise StopConsumer() |

本文作者:岳宗柯
本文链接:https://www.cnblogs.com/yuezongke/p/17604561.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步