zoukankan      html  css  js  c++  java
  • 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.效果

  • 相关阅读:
    【笔记】二进制文件
    vs2015+64位win10系统ceres-solver编译
    python
    感受函数式编程-scala
    R语言diagram包画订单状态流图
    virtualbox下Centos6.5桥接模式上网配置方法
    配置对IIS上tabular的 HTTP 访问
    centos6.5下逻辑卷操作
    centos6.5下磁盘创建交换分区
    centos6.5下磁盘分区及挂载
  • 原文地址:https://www.cnblogs.com/chenjw-note/p/12161554.html
Copyright © 2011-2022 走看看