• Django使用Channels实现WebSocket


    WebSocket是什么?

    WebSocket是一种在单个TCP连接上进行全双工通讯的协议。WebSocket允许服务端主动向客户端推送数据。在WebSocket协议中,客户端浏览器和服务器只需要完成一次握手就可以创建持久性的连接,并在浏览器和服务器之间进行双向的数据传输。

    WebSocket有什么用?

    WebSocket区别于HTTP协议的一个最为显著的特点是,WebSocket协议可以由服务端主动发起消息,对于浏览器需要及时接收数据变化的场景非常适合,例如在Django中遇到一些耗时较长的任务我们通常会使用Celery来异步执行,那么浏览器如果想要获取这个任务的执行状态,在HTTP协议中只能通过轮训的方式由浏览器不断的发送请求给服务器来获取最新状态,这样发送很多无用的请求不仅浪费资源,还不够优雅,如果使用WebSokcet来实现就很完美了

    WebSocket的另外一个应用场景就是下文要说的聊天室,一个用户(浏览器)发送的消息需要实时的让其他用户(浏览器)接收,这在HTTP协议下是很难实现的,但WebSocket基于长连接加上可以主动给浏览器发消息的特性处理起来就游刃有余了

    初步了解WebSocket之后,我们看看如何在Django中实现WebSocket

    Channels

    Django本身不支持WebSocket,但可以通过集成Channels框架来实现WebSocket

    Channels是针对Django项目的一个增强框架,可以使Django不仅支持HTTP协议,还能支持WebSocket,MQTT等多种协议,同时Channels还整合了Django的auth以及session系统方便进行用户管理及认证。

    我下文所有的代码实现使用以下python和Django版本

    • python==3.6.3

    • django==2.2

    一、集成channels,实现websocket聊天室

      1.项目结构 webapp_channels_websocket

      

       2.安装channels

    pip install channels==2.1.7

      3.修改settings.py文件

    #注册app
    INSTALLED_APPS = [ ...... 'channels', 'chat', ] # 指定ASGI的路由地址 ASGI_APPLICATION = 'webapp_channels_websocket.routing.application'
    #指定redis [channel layer是一种通信系统,允许多个consumer实例之间互相通信,以及与外部Djanbo程序实现互通] CHANNEL_LAYERS
    = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('127.0.0.1', 6379)], }, }, }

      4.编写路由文件webapp_channels_websocket/routing.py

    from channels.auth import AuthMiddlewareStack
    from channels.routing import ProtocolTypeRouter, URLRouter
    import chat.routing
    
    application = ProtocolTypeRouter({
        'websocket': AuthMiddlewareStack(
            URLRouter(
                chat.routing.websocket_urlpatterns
            )
        ),
    })

      5.编写路由文件chat/routing.py

    from django.urls import path
    from chat.consumers import ChatConsumer
    
    websocket_urlpatterns = [
        path('ws/chat/', ChatConsumer),
    ]

      6.编写chat/consumer.py

    import json
    from channels.generic.websocket import AsyncWebsocketConsumer   #异步,实现更好的性能
    
    class ChatConsumer(AsyncWebsocketConsumer):
        async def connect(self):
            # connect方法在连接建立时触发
            self.room_group_name = 'chat_test'
    
            # Join room group
            await self.channel_layer.group_add(
                self.room_group_name,
                self.channel_name
            )
            await self.accept()
    
        async def disconnect(self, close_code):
            # disconnect在连接关闭时触发
            # Leave room group
            await self.channel_layer.group_discard(
                self.room_group_name,
                self.channel_name
            )
    
        # Receive message from WebSocket
        async def receive(self, text_data):
            # receive方法会在收到消息后触发
            text_data_json = json.loads(text_data)
            message = text_data_json['message']
    
            # Send message to room group
            await self.channel_layer.group_send(
                self.room_group_name,
                {
                    'type': 'chat_message',
                    'message': message
                }
            )
    
        # Receive message from room group
        async def chat_message(self, event):
            message = '测试聊天室:' + event['message']
    
            # Send message to WebSocket 【最后在这里发送返回前端页面】
            await self.send(text_data=json.dumps({
                'message': message
            }))

      7.编写url  webapp_channels_websocket/urls.py

    from django.contrib import admin
    from django.urls import path
    from chat import views as chat_views
    
    urlpatterns = [
        path('chat/', chat_views.chat),
    ]

      8.编写视图函数 chat/views.py

    from django.shortcuts import render
    
    def chat(request):
        return render(request, 'index.html')

      9.编写前端文件templates/index.html

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Title</title>
    </head>
    <body>
          <textarea class="form-control" id="chat-log" disabled rows="20"></textarea><br/>
          <input class="form-control" id="chat-message-input" type="text"/><br/>
          <input class="btn btn-success btn-block" id="chat-message-submit" type="button" value="Send"/>
    
            <script>
              var chatSocket = new WebSocket('ws://' + window.location.host + '/ws/chat/');
    
              chatSocket.onmessage = function(e) {
                var data = JSON.parse(e.data);
                var message = data['message'];
                document.querySelector('#chat-log').value += (message + '
    ');
              };
    
              chatSocket.onclose = function(e) {
                console.error('Chat socket closed unexpectedly');
              };
    
              document.querySelector('#chat-message-input').focus();
              document.querySelector('#chat-message-input').onkeyup = function(e) {
                if (e.keyCode === 13) {  // enter, return
                    document.querySelector('#chat-message-submit').click();
                }
              };
    
              document.querySelector('#chat-message-submit').onclick = function(e) {
                var messageInputDom = document.querySelector('#chat-message-input');
                var message = messageInputDom.value;
                chatSocket.send(JSON.stringify({
                    'message': message
                }));
    
                messageInputDom.value = '';
              };
            </script>
    </body>
    </html>

      10.运行

       11.效果

  • 相关阅读:
    Razor 视图引擎的一些属性和方法
    Asp.Net Mvc Razor
    微信小程序时代已经来临
    ionic常用命令记录
    ionic中将service中异步返回的数据赋值给controller的$scope
    nodejs连接mysql实例
    转载:ionic+nodejs开发遇到的跨域和post请求数据问题
    NodeJs-- 新建项目实例
    Bootstrap学习指南
    ios开发环境配置及cordova安装与常用命令
  • 原文地址:https://www.cnblogs.com/chenjw-note/p/12161554.html
Copyright © 2020-2023  润新知