WebSocket是什麼?html
WebSocket是一種在單個TCP鏈接上進行全雙工通信的協議。WebSocket容許服務端主動向客戶端推送數據。在WebSocket協議中,客戶端瀏覽器和服務器只須要完成一次握手就能夠建立持久性的鏈接,並在瀏覽器和服務器之間進行雙向的數據傳輸。前端
WebSocket有什麼用?python
WebSocket區別於HTTP協議的一個最爲顯著的特色是,WebSocket協議能夠由服務端主動發起消息,對於瀏覽器須要及時接收數據變化的場景很是適合,例如在Django中遇到一些耗時較長的任務咱們一般會使用Celery來異步執行,那麼瀏覽器若是想要獲取這個任務的執行狀態,在HTTP協議中只能經過輪訓的方式由瀏覽器不斷的發送請求給服務器來獲取最新狀態,這樣發送不少無用的請求不只浪費資源,還不夠優雅,若是使用WebSokcet來實現就很完美了web
WebSocket的另一個應用場景就是下文要說的聊天室,一個用戶(瀏覽器)發送的消息須要實時的讓其餘用戶(瀏覽器)接收,這在HTTP協議下是很難實現的,但WebSocket基於長鏈接加上能夠主動給瀏覽器發消息的特性處理起來就遊刃有餘了redis
初步瞭解WebSocket以後,咱們看看如何在Django中實現WebSocketdjango
Django自己不支持WebSocket,但能夠經過集成Channels框架來實現WebSocketjson
Channels是針對Django項目的一個加強框架,可使Django不只支持HTTP協議,還能支持WebSocket,MQTT等多種協議,同時Channels還整合了Django的auth以及session系統方便進行用戶管理及認證。瀏覽器
我下文全部的代碼實現使用如下python和Django版本服務器
python==3.6.3websocket
django==2.2
1、集成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.效果