django中配置websocket

     阅读:50

python本身只支持http协议 使用websocket需要下载第三方库

pip install -U channels
  • 在安装在windows机器的时候。需要自信的C++支持,报错的时候,报错有地址告诉你下载URL

配置

  • 需要在seting.py里配置,将我们的channels加入INSTALLED_APP里。
INSTALLED_APPS = (
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.sites',
    ...
    'channels',
)
ASGI_APPLICATION = 'bug_Project_name.asgi.application'
  • 修改asgi.py文件
import os
from channels.routing import ProtocolTypeRouter,URLRouter
from django.core.asgi import get_asgi_application
from . import routing
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'bug_Project2.settings')
#application = get_asgi_application()
application = ProtocolTypeRouter({
   "http":get_asgi_application(),
   "websockt":URLRouter(routing.websoctet_urlpatterns )
})
  • 在settings.py的同级目录创建routing.py
from django.urls import re_path
from web.views import consumers

websoctet_urlpatterns = [
    re_path('ws/(?P<group>\w+)/$',consumers.ChatConsumer.as_asgi()),
] 
  • 在web.views中创建consumers.py
# -*- coding:UTF-8 -*-

# author:Administrator
# contact: 913632472@qq.com
# datetime:2022/1/10 11:55
# software: PyCharm

"""
文件说明:
"""

from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer

class ChatConsumer(WebsocketConsumer):

    def websocket_connect(self,message):
        # 有客户端来向后端发送websocket连接请求时,自动触发
        # 后端允许客户端创建连接
        print('有人来了')
        self.accept()
        #给客户端发送小时
        self.send("欢迎啊")

    def websocket_receive(self, message):
        # 浏览器基于websocket向后端发送数据,自动触发接受消息
        text = message['text']
        print("接受到消息-->", text)
        res = "{}SB".format(text)
        if text == "关闭":
            # 客户端通过发送命令 主动断开连接
            self.close()
            print('通过命令关闭')
            raise StopConsumer # 如果服务端断开连接,执行StopConsumer异常 那么websocket_disconnect不会执行
        self.send(res)
        # self.close() #后端主动断开websocket


    def websocket_disconnect(self, message):
        # 客户端与服务端断开连接,自动触发
        print("客户端断开连接")
        raise StopConsumer


django中需要了解的

  • wsgi,只支持http请求
  • asgi,wsgi+异步+websockt
    ASGI启动成功

简单使用前端页面测试websocket

<html lang="en">
<head>
 <meta charset="UTF-16">
 <title>title</title>
 <style>
     .message{
         height: 300px;
         border: 1px solid #dddddd;
         width: 100%;
     }
 </style>
</head>
</html>
<div class="message" id="message"></div>
<div>
 <input type="text" placeholder="请输入" id="txt">
 <input type="button" value="发送" onclick="sendMessage()">
</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);

 }

 //当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>

群聊

  • 修改配置
"""基于内存实现"""
 CHANNEL_LAYERS = {
     "default": {
         "BACKEND": "channels.layers.InMemoryChannelLayer",
   }
 }
"""基于redis实现"""
CHANNEL_LAYERS = {
    "default": {
        "BACKEND": "channels_redis.pubsub.RedisPubSubChannelLayer",
        "CONFIG": {
            "hosts": [("localhost", 6379)],
        },
    },
}

Channels引入了一个layer的概念,channel layer是一种通信系统,允许多个consumer实例之间互相通信,以及与外部Djanbo程序实现互通。
channel layer主要实现了两种概念抽象:

channel name: channel实际上就是一个发送消息的通道,每个Channel都有一个名称,每一个拥有这个名称的人都可以往Channel里边发送消息

group: 多个channel可以组成一个Group,每个Group都有一个名称,每一个拥有这个名称的人都可以往Group里添加/删除Channel,也可以往Group里发送消息,Group内的所有channel都可以收到,但是无法发送给Group内的具体某个Channel

了解了上边的概念,接下来我们利用channel layer实现真正的聊天室,能够让多个客户端发送的消息被彼此看到
官方推荐使用redis作为channel layer,所以先安装channels_redis

pip install channels_redis
# 基于同步实现通信
from channels.generic.websocket import WebsocketConsumer
from channels.exceptions import StopConsumer
from asgiref.sync import async_to_sync #把django的异步切换成同步

class ChatConsumer(WebsocketConsumer):


    def websocket_connect(self,message):
        # 获取群号,获取路由匹配中的
        self.accept()
        group=self.scope["url_route"]['kwargs'].get('group')
        print('WebSocket建立连接:', group)
        #将客户端的连接对象加入到某个地方 (内存 or redis)
        async_to_sync(self.channel_layer.group_add)(group,self.channel_name)

    def websocket_receive(self, message):
        print("fas")
        group = self.scope["url_route"]['kwargs'].get('group')
        #通知组内的所有客户端 执行xx.oo的方法,在次方法可以自己去定义任意的功能
        async_to_sync(self.channel_layer.group_send)(group,{"type":"xx.oo",'message':message})



    def xx_oo(self,event):
        text = event['message']['text']
        self.send(text)

    def websocket_disconnect(self, message):
        # 客户端与服务端断开连接,自动触发
        print("客户端断开连接")
        group = self.scope["url_route"]['kwargs'].get('group')
        async_to_sync(self.channel_layer.group_discard)(group,self.channel_name)
        raise StopConsumer