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 + '\n');
          };

          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.效果

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM