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/" - -

 

posted @ 2022-02-21 23:36  dos_hello_world  阅读(288)  评论(0)    收藏  举报