Django实现websocket聊天室
什么是WebSocket
簡介
WebSocket協議是基于TCP的一種新的網絡協議。它實現了瀏覽器與服務器雙向通信,即允許服務器主動發送信息給客戶端。因此,在WebSocket中,瀏覽器和服務器只需要完成一次握手,兩者之間就直接可以創建持久性的連接,并進行雙向數據傳輸,客戶端和服務器之間的數據交換變得更加簡單。
WebSocket的優勢
現在,很多網站為了實現推送技術,所用的技術都是Ajax輪詢。輪詢是在特定的時間間隔(如每1秒),由瀏覽器對服務器發出HTTP請求,然后由服務器返回最新的數據給客戶端的瀏覽器。
這種傳統的模式帶來很明顯的缺點,即瀏覽器需要不斷的向服務器發出請求。然而HTTP請求可能包含較長的頭部,其中真正有效的數據可能只是很小的一部分,顯然這樣會浪費很多的帶寬等資源。HTML5定義的WebSocket協議優勢如下:
- 小Header:互相溝通的Header非常小,只有2Bytes左右。
- 服務器不再被動接收到瀏覽器的請求之后才返回數據,而是在有新數據時就主動推送給瀏覽器。
- WebSocket協議能更好的節省服務器資源和帶寬,并且能夠更實時地進行通訊。
WebSocket可以幫助兩端或多端接入的用戶實時傳遞信息可以簡單的實現一個聊天室,既可以是
一對一的聊天也可以是多對多的聊天。舉個例子:我們在看直播的時候的實時彈幕就可以通過WebSocke實現,以及我們在微信上和某個人聊天的場景也可以通過WebSocke實現
我們要想通過django和實現聊天首先我們還需要借助一個工具來幫助我們實現,使用channels可以讓你的Django應用擁有實時通訊和給用戶主動推送信息的功能。
準備階段
django配置
1、安裝指定版本的包
pip install channels==2.1.3 pip install channels_redis2、注冊channels
INSTALLED_APPS = ['django.contrib.admin','django.contrib.auth','django.contrib.contenttypes','django.contrib.sessions','django.contrib.messages','django.contrib.staticfiles','rest_framework','corsheaders','channels','drf_yasg2', ]3、在setting中配置asgi
這里為什么用asgi不用wsgi,因為wsgi不支持websocket通信。以及配置redis數據庫
WSGI_APPLICATION = '項目名.wsgi.application' ASGI_APPLICATION = '項目名.asgi.application'CHANNEL_LAYERS = {'default': {'BACKEND': 'channels_redis.core.RedisChannelLayer','CONFIG': {"hosts": [('127.0.0.1', 6379)],},}, }4、修改asgi.py文件
? ? ? ? 需要注意的是,django版本小于3.0.0的話,需要自己創建一個asgi.py文件,django版本大于3.0.0就不用擔心此問題,只需要修改asgi.py文件中的某些代碼即可
""" ASGI config for shixun project.It exposes the ASGI callable as a module-level variable named ``application``.For more information on this file, see https://docs.djangoproject.com/en/3.2/howto/deployment/asgi/ """import os import django from channels.http import AsgiHandler from channels.routing import ProtocolTypeRouter, URLRouter from . import routingos.environ.setdefault('DJANGO_SETTINGS_MODULE', '項目名.settings') django.setup()application = ProtocolTypeRouter({"http": AsgiHandler(),"websocket": URLRouter(routing.websocket_urlpatterns), })5、在settings同級目錄中創建routing.py。注冊路由
from django.urls import path from shixun.consumers import ChatConsumerwebsocket_urlpatterns = [path('ws/chat', ChatConsumer.as_asgi()), ]6、創建consumer.py文件,處理websocket通信邏輯
import jsonfrom asgiref.sync import async_to_sync from channels.generic.websocket import WebsocketConsumer from channels.exceptions import StopConsumerCONN_LIST = []class ChatConsumer(WebsocketConsumer):def websocket_connect(self, message):print("開始鏈接...")# 有客戶端來向后端發送websocket連接的請求時,自動觸發。# 服務端允許和客戶端創建連接(握手)。self.accept()CONN_LIST.append(self)def websocket_receive(self, message):print('22222222', message)data = json.loads(message.get('text', '{}'))chat_type = data.get('chat_type')chat_id = data.get('chat_id')chat_content = data.get('message')# 判斷聊天室的類型是否為加入聊天室,若為加入聊天室,就獲取到聊天室的id和名字if chat_type == 'add_chat':async_to_sync(self.channel_layer.group_add)(chat_id, self.channel_name)else:async_to_sync(self.channel_layer.group_send)(chat_id, {"type": 'chat.message', 'message': message})def chat_message(self, event):# 發送消息的類型為textself.send(event['message']['text'])# def websocket_receive(self, message):# # 瀏覽器基于websocket向后端發送數據,自動觸發接收消息。# print('接受的消息', message)# text = message['text'] # {'type': 'websocket.receive', 'text': ''}# print("接收到消息-->", text)# res = {'result': 'ok'}# for conn in CONN_LIST:# conn.send(json.dumps(res))# 關閉連接def websocket_disconnect(self, message):CONN_LIST.remove(self)raise StopConsumer()?前端
我們在使用vue的時候需要使用一個插件就可以輕松實現我們想要的效果,如圖
?
使用chat_ui插件
1、安裝
npm install @maybecode/m-chat?2、vue中添加(main.js引用一下)
import Vue from 'vue' import MChat from '@maybecode/m-chat'Vue.use(MChat)3、 局部安裝(在我們使用的時候我們需要導入一下)
import MChat from '@maybecode/m-chat'export default {components: {MChat} }?4、前端代碼
<template><div style="width: 60%; margin-left: 20%"><div class="message" id="message"></div><div v-show="add_chat"><inputtype="text"placeholder="請輸入群聊id"id="txt"v-model="chat_id"/><input type="button" value="加入群聊" @click="addChat()" /></div><div v-show="send_chat"><m-chatref="mChat":messages="messages"@submit="submit":loadMore="loadMore"height="100vh":openExtends="open_extends"></m-chat></div></div> </template><script> // import { } from "@/components/axios_api/api.js"; // @符號指的是src路徑 import MChat from "@maybecode/m-chat"; export default {data() {return {user_id: "",send_data: "",chat_id: null,send_chat: false,add_chat: true,messages: [],open_extends: ["image"],};},components: {MChat,},methods: {submit(content) {this.sendMessage(content["content"]);},loadMore() {},// 加入群聊,如果有對應的id,進入群聊,沒有則創建一個新的群聊addChat() {if (this.chat_id) {this.initWebSocket();this.send_chat = true;this.add_chat = false;} else {alert("請輸入群聊號");}},initWebSocket() {//初始化websocketvar wsuri = "ws://ip地址:8000";// var ws_scheme = window.location.protocol === "https:" ? "wss" : "ws";var ws_on_line = wsuri + "/ws/chat";// 本地走代理/vue.config.js,線上也代理nginx代理console.log("111111111111", ws_on_line);// var ws_scheme = window.location.protocol==="https:"?"wss":"ws"this.websock = new WebSocket(ws_on_line);this.websock.onopen = this.websocketonopen;this.websock.onmessage = this.websocketonmessage;this.websock.onerror = this.websocketonerror;// this.websock.onclose = this.websocketclose;},websocketonopen() {//連接建立之后執行send方法發送數據console.log("建立連接");var actions = { message: "連接測試" };var type = "add_chat";this.sendMessage(actions, type);},websocketonerror(e) {//連接建立失敗重連console.log("連接error", e);// this.initWebSocket();},websocketonmessage(e) {//數據接收this.websocket_data = JSON.parse(e.data);console.log("websocket-res", JSON.stringify(this.websocket_data));console.log("接收后端數據type", typeof this.websocket_data);let message_user_id = this.websocket_data["user_id"];let message_self = false;// 判斷當前消息是否自己發出if (message_user_id == localStorage.getItem("user_id")) {message_self = true;console.log(">>>>>>>>>>>>>>message_user_id",message_user_id,localStorage.getItem("user_id"));}let name = this.websocket_data.username;// 將websocket消息展示在消息提示框var count = this.messages.length;var push_message = {type: "text",content: { text: JSON.stringify(this.websocket_data.message) },id: count + 1,self: message_self,name: name,};this.messages.push(push_message);var h = this.$createElement;// 創建html元素this.hrender = h("p", null, [h("div",[h("div", JSON.stringify(this.websocket_data.message)),// 傳變量// },// }),],null),]);},sendMessage(Data, type = "send_data") {//數據發送console.log("222222222222");this.websock.send(JSON.stringify({chat_id: this.chat_id,message: Data,chat_type: type,username: localStorage.getItem("username"),user_id: localStorage.getItem("user_id"),}));},websocketclose(e) {//關閉console.log("斷開連接", e);},userinfo() {},},mounted() {}, }; </script><style> </style>下方表格是chat_ui的一些屬性,我們可以根據需求來進行選擇
| messages | Array | [] | 消息數組 |
| height | String | 100vh | 容器高度 |
| loadMore | Function | - | 加載更多函數 |
| defaultAvatar | String | - | 默認頭像(支持本地頭像require導入或者地址) |
| comment | Boolean | true | 是否顯示回復框 |
| openPops | Array | ['copy', 'cancel'] | 氣泡框內顯示的功能 例: ['copy', 'cancel'] |
| customRecord | Boolean | flase | 自定義錄音功能(設置為true后自帶錄音失效,并且不觸發submit事件) |
| openExtends | Array | ["image", "file", "video"] | 擴展面板顯示的功能 |
| openBases | Array | ["text", "audio", "emoji"] | 控制基礎功能顯示 |
| imgMaxSize | Number | 500 | 圖片大小上傳限制(kb) |
| videoMaxSize | Number | 500 | 視頻大小上傳限制(kb) |
| fileMaxSize | Number | 500 | 文件大小上傳限制(kb) |
| leadPage | String | - |
?若想要與小伙伴們一起測試的話可以在初始化初始化websocket是wsurl改為項目相對應的ip地址
前端改完這個地址,對應的后端也要改一下運行的方式:python manage.py runserver 0.0.0.0:8000 即可讓其他的小伙伴也一起進行聊天
注:
`aioredis.errors.ReplyError: ERR unknown command 'BZPOPMIN'`
如果你的項目在運行過程中遇到此問題,是由于redis的版本過低,將redis的版本版本升級到5.0.0以及即可
Releases · tporadowski/redis · GitHubNative port of Redis for Windows. Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, Streams, HyperLogLogs. This repository contains unofficial port of Redis to Windows. - Releases · tporadowski/redishttps://github.com/tporadowski/redis/releases上邊是redis官網的地址,如遇此問題請自行下載
切記:前端初始化websocket時的路由必須和后端啟動方式相對應,否則也會報錯,如果初始化的是ip地址,則后端啟動時一定要在后邊加上0.0.0.0:8000
?
?
總結
以上是生活随笔為你收集整理的Django实现websocket聊天室的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: pdf转jpg怎么转呢?
- 下一篇: 关于正向代理,反向代理,负载均衡的个人理