django-channel实现实时聊天
环境准备:
安装 python 依赖库:
pip install channels pip install channels_redis pip install pypiwin32
pip install daphne
安装 docker 版本的 redis 服务
docker pull redis docker run --name redis -p 6379:6379 -d redis
建立 django 项目
django-admin startproject mydaphne
建立本次测试使用的项目 APP
django-admin createapp chat
ASGI 环境代码准备
websocket 是一个独立于 django web app 的一个应用程序,需要单独启动。不同于 python web app 需要 wsgi 做协以转换,websocket 是基于 asgi 实现协以转换。
该部分内容实现了以下内容:
1、为 websocket 准备消费者代码
2、准备 websocket 的后端服务
添加 ASGI 支持:mydaphne/settings.py
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', 'mydaphne', 'rest_framework', 'channels', 'chat' ] WSGI_APPLICATION = 'mydaphne.wsgi.application' ASGI_APPLICATION = 'mydaphne.routing.application' CHANNEL_LAYERS = { "default": { "BACKEND": "channels_redis.core.RedisChannelLayer", "CONFIG": { "hosts": [('127.0.0.1', 6379)], } } }
添加 websocket 的路由:mydaphne/routing.py
from channels.auth import AuthMiddlewareStack from channels.routing import ProtocolTypeRouter from channels.routing import URLRouter from mydaphne.urls import websocket_urlpatterns application = ProtocolTypeRouter({ 'websocket': AuthMiddlewareStack( URLRouter( websocket_urlpatterns ) ) })
mydaphne/urls.py
"""mydaphne URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path
from django.urls import include
from mydaphne.consumers import ChatConsumer
urlpatterns = [
path('admin/', admin.site.urls),
path('index/', include('website.urls')),
path('chat/', include(('chat.urls', 'chat'), namespace='chat'))
]
websocket_urlpatterns = [
path('ws/chat/<room_name>/', ChatConsumer.as_asgi()),
]
websocket 的消费者代码:mydaphne/consumers.py
from channels.generic.websocket import AsyncWebsocketConsumer
import 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, code):
await self.channel_layer.group_discard(
self.room_group_name,
self.channel_name
)
async def receive(self, text_data=None, bytes_data=None):
text_data_json = json.loads(text_data)
message = text_data_json['message']
await self.channel_layer.group_send(
self.room_group_name,
{
'type': 'chat_message',
'message': message
}
)
async def chat_message(self, event):
message = event['message']
await self.send(text_data=json.dumps({
'message': message
}))
准备 websocket 入口:mydaphne/asgi.py
异步通讯时,使用的就是这个asgi。asgi兼容wsgi,所以启动asgi后,普通的WEB程序,也是可以使用的。但是很多情况下,回将异步任务(WS)和HTTP普通请求分开,建立不同的服务器,但是代码还是一套代码。这里不介绍,如何实现这个。
import os
import django
from channels.routing import get_default_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mydaphne.settings')
django.setup()
application = get_default_application()
业务代码准备
chat/urls.py
from django.urls import path
from website.views import Index
from .views import *
urlpatterns = [
path('', newChat, name='newChat'),
path('<room_name>/', room, name="room")
]
chat/views.py
from django.shortcuts import render
# Create your views here.
def newChat(request):
return render(request, 'chat.html', locals())
def room(request, room_name):
return render(request, 'room.html', locals())
chat.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Chat Rooms</title>
</head>
<body>
<div>请输入聊天室名称</div>
<br/>
<input id="input" type="text" size="30" />
<br/>
<input id="submit" type="button" value="进入">
<script>
document.querySelector("#input").focus();
document.querySelector('#input').onkeyup = function (e) {
if (e.keyCode === 13) {
document.querySelector('#submit').click();
}
};
document.querySelector('#submit').onclick = function (e) {
var roomName = document.querySelector('#input').value;
window.location.pathname = 'chat/' + roomName + '/';
};
</script>
</body>
</html>
websocket 的生产者:room.html
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>room</title> </head> <body> <label for="chat-log"></label><textarea id="chat-log" cols="50" rows="6"></textarea> <br/> <label for="input"></label><input id="input" type="text" size="50" /> <br/> <input id="submit" type="button" value="发送" /> </body> <script> var roomName = '{{ room_name }}'; var chatSocket = new WebSocket( 'ws://' + window.location.host + "/ws/chat/" + roomName + '/'); 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('#input').focus(); document.querySelector('#input').onkeyup = function (e) { if(e.keyCode === 13) { document.querySelectorAll('#submit').click(); } }; document.querySelector('#submit').onclick = function (e) { var messageInputDom = document.querySelector('#input'); var message = messageInputDom.value; chatSocket.send(JSON.stringify({ 'message': message })); messageInputDom.value = ""; }; </script> </html>
启动websocket 服务
使用指令在本地启动 websocket 服务
(daphne) ➜ mydaphne daphne mydaphne.asgi:application 2022-02-21 15:13:51,196 INFO Starting server at tcp:port=8000:interface=127.0.0.1 2022-02-21 15:13:51,197 INFO HTTP/2 support not enabled (install the http2 and tls Twisted extras) 2022-02-21 15:13:51,197 INFO Configuring endpoint tcp:port=8000:interface=127.0.0.1 2022-02-21 15:13:51,197 INFO Listening on TCP address 127.0.0.1:8000 127.0.0.1:60325 - - [21/Feb/2022:15:13:59] "GET /chat/" 200 694 127.0.0.1:60325 - - [21/Feb/2022:15:14:01] "GET /chat/lsl/" 200 1327 127.0.0.1:60327 - - [21/Feb/2022:15:14:01] "WSCONNECTING /ws/chat/lsl/" - - 127.0.0.1:60327 - - [21/Feb/2022:15:14:01] "WSCONNECT /ws/chat/lsl/" - -

浙公网安备 33010602011771号