查看原文
其他

Django使用WebSocket实现富婆聊天群

阿良 DevOps技术栈 2022-11-10

WebSocket协议与HTTP的主要区别:HTTP是无状态协议,由客户端发起请求,客户端与服务器“一问一答”,因此服务器端无法主动向客户端发送信息。而WebSocket是基于TCP长连接的协议,客户端与服务器建立连接后,服务器端随时能向客户端发送信息。

WebSocket协议的主要价值在于其与HTTP的差异(服务器端与客户端能够保持实时的双向通信),使其在某些应用情景下比HTTP更能满足技术需求。


Django Web框架实现WebSocket主要有两种方式:channels和dwebsocket。

Channels是针对Django项目的一个增强框架,使得Django不仅支持HTTP协议,还能支持WebSocket协议, 所以本文使用Channels来实现即时通讯,开发一个聊天室,使得每个人发送的消息都能实时显示在别人的屏幕上


软件环境:

Python:3.7

Django:3.0

一、安装模块
pip install channelspip install channels_redis

二、创建一个Django项目和一个APP

项目名为django_ws_demo,APP名为chat,目录如下:


三、开发首页

3.1 创建前端页面

在templates文件夹创建chat文件夹,用来存放聊天室页面。

templates/chat/index.html页面内容如下:

<!DOCTYPE html><html><head> <meta charset="utf-8"/> <title>聊天室</title></head><body> 你想进入哪个富婆群?<br/> <input id="room-name-input" type="text" /> <input id="room-name-submit" type="button" value="确定"/>
<script> document.querySelector('#room-name-submit').onclick = function(e) { var roomName = document.querySelector('#room-name-input').value; window.location.pathname = '/chat/' + roomName + '/'; };</script></body></html>

3.2 添加视图函数

chat/views.py中添加视图函数:

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

3.3 注册路由

在项目路由中django_ws_demo/urls.py包含chat url

from django.contrib import adminfrom django.urls import path, include, re_path
urlpatterns = [ path('admin/', admin.site.urls), re_path('^chat/', include('chat.urls')),]

创建chat/urls.py文件并设置路由信息:

from django.urls import re_path
from . import views
urlpatterns = [ re_path(r'^$', views.index, name='index'),]

启动Django,打开浏览器正常会看到以下页面:


四、开发聊天室

4.1 创建聊天窗口页面

templates/chat/room.html页面内容如下:

<!DOCTYPE html><html><head> <meta charset="utf-8"/> <title>聊天室</title></head><body> <h2>北京富婆群</h2> <textarea id="chat-log" cols="100" rows="20" style="font-size: 18px;color: red;font-weight: bold"></textarea><br/> <input id="chat-message-input" type="text" size="50"/> <input id="chat-message-submit" type="button" value="发送"/></body><script> var num = parseInt(100*Math.random()); var roomName = {{ room_name_json }};
// 浏览器通过 JavaScript 向服务器发出建立 WebSocket 连接的请求 var ws = new WebSocket( 'ws://' + window.location.host + '/ws/chat/' + roomName + '/');
// 监听按钮 document.querySelector('#chat-message-submit').onclick = function(e) { // 获取输入框数据 var messageInputDom = document.querySelector('#chat-message-input'); var message = messageInputDom.value;
console.log(num); // 使用send()方法发送数据 ws.send(JSON.stringify({ 'user': num, 'message': message }));
// 输入框值设置为空 messageInputDom.value = ''; // 通过 onmessage 事件来接收服务器返回的数据。 ws.onmessage = function(e) { var data = JSON.parse(e.data); var user = data['user']; console.log(e.data); var message = data['message'];
document.querySelector('#chat-log').value += ('匿名富婆' + user + ': ' + message + '\n'); }; // 关闭 websocket ws.onclose = function(e) { console.error('聊天套接字意外关闭!'); }; };</script></html>

4.2 添加视图函数

chat/views.py中添加一个处理 room 的视图函数:

from django.shortcuts import renderfrom django.utils.safestring import mark_safeimport json
def index(request): return render(request, 'chat/index.html', {})
def room(request, room_name): return render(request, 'chat/room.html', { 'room_name_json': mark_safe(json.dumps(room_name))    })

4.3 注册路由

chat/urls.py中注册路由

from django.urls import re_path
from . import views
urlpatterns = [ re_path(r'^$', views.index, name='index'), re_path(r'^(?P<room_name>[^/]+)/$', views.room, name='room'),]

五、配置Django Channels

5.1 配置Channels

在 settings.py文件中注册channels

INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'channels', 'chat']

并在下面直接添加一行:

ASGI_APPLICATION = 'django_ws_demo.routing.application'

5.2 配置路由

settings.py文件同级目录下新建routing.py路由文件,内容如下:

from channels.auth import AuthMiddlewareStackfrom channels.routing import ProtocolTypeRouter, URLRouterfrom django.urls import re_pathfrom .import consumers
application = ProtocolTypeRouter({ 'websocket': AuthMiddlewareStack( URLRouter([ re_path(r'^ws/chat/(?P<room_name>[^/]+)/$', consumers.ChatConsumer), ]) ),})

正则匹配所有以ws/chat开头的websocket连接,都交由名为consumersChatConsumer处理。

5.3 WebSocket服务端消费

ChatConsumer代码文件路径为django_ws_demo/consumers.py,内容如下:

from channels.generic.websocket import AsyncWebsocketConsumerimport json
class ChatConsumer(AsyncWebsocketConsumer): async def connect(self): self.room_name = self.scope['url_route']['kwargs']['room_name'] self.room_group_name = 'chat_%s' % self.room_name
# 加入聊天室 await self.channel_layer.group_add( self.room_group_name, self.channel_name )
await self.accept()
# 离开聊天室 async def disconnect(self, close_code):
await self.channel_layer.group_discard( self.room_group_name, self.channel_name )
# 接收来自WebSocket的消息 async def receive(self, text_data): text_data_json = json.loads(text_data) user = text_data_json['user'] message = text_data_json['message']
# 发送消息到聊天室 await self.channel_layer.group_send(
self.room_group_name, { 'type': 'chat_message', 'user': user, 'message': message } )
# 接受来自聊天室的消息 async def chat_message(self, event): message = event['message'] user = event['user'] # 发送消息到WebSocket await self.send(text_data=json.dumps({ 'user': user, 'message': message }))

5.4 配置Redis

settings.py配置文件添加redis配置,内容如下:

CHANNEL_LAYERS = { 'default': { 'BACKEND': 'channels_redis.core.RedisChannelLayer', 'CONFIG': { "hosts": [('192.168.31.61', 6379)], }, },}

settings.py总添加配置如下:



整体目录结构:


最后,访问测试



- END -

推荐阅

Kubernetes CKA 官方认证(本周开班)

从 kubectl top 看 Kubernetes 监控原理

最易懂的数据库异地多活方案

Kubernetes 数据库 Etcd 日常运维及技巧

Kubernetes 容器云平台技术方案

全网最详细的 K8s Service 不能访问排查流程

企业 MySQL 优化实施方案

疫情之下,人人都需要一技傍身,是时候拿下K8s了



年轻时偷的懒,迟早是要还的。点亮

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存