websocke在django中使用

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
  1. 127,2字节,8个字节,其他字节 (4字节 masking key + 数据)
  2. 126,2字节,2个字节,其他字节 (4字节 masking key +数据)
  3. <=125,2字节
  • 其他字节 (4字节 masking key + 数据)
  • 获取maskingkey,然后对数据进行解密
var DECODED ='';
for (var i = 0; < ENCODED.length; i++) {
(DECODED[I] = ENCODED[i]MASK^[i% 4]
}

-断开连接
image

二、后端

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 = get_asgi_application()
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()

image

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/")

    // 创建好连接之后自动触发(服务端执行self.accept())
    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);}

        // 当websocket接受到服务端发来的消息时,自动会触发这个函数
        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']  # {'type': 'websocket.receive', 'text': '11'}
        print("接收到消息---->", text)

        if text == '关闭':
            self.close()
            # raise StopConsumer()  # 如果服务端断开连接时,执行这个方法异常,那么websocket_disconnect方法不再执行
            return
        res = '{}sb'.format(text)
        # 服务端主动关闭连接,给客户端发送一条断开连接的消息
        self.send(res)


def websocket_disconnect(self, message):
    '''客户端断开链接之后  自动触发'''
    print('断开链接')
    raise StopConsumer()

image

posted @ 2023-08-03 21:37  岳宗柯  阅读(47)  评论(1编辑  收藏  举报