Django插件Channels ——实现即时通信

注明

本笔记主要参考《Django应用开发实战》《Django企业开发实战》,这两本书前者详细,后者精炼。学习之后真的是感觉自己进步了很多。值得一读,如果您遇到了值得一读的书籍,欢迎推荐给我,大家共同进步。


@[TOC](文章目录)

前言

Web聊天室的实现方法有多种,下面将简要介绍一下实现方法

  • AJAX技术利用AJAX实现网页与服务器的无刷新交互。缺点是实时性不高。
  • Coment技术Coment是一种Web应用框架,服务器以异步方式向浏览器推送数据,无需浏览器发送请求,非常适合事件驱动的Web应用,以及对交互性和实时性要求较高的应用。
  • XMPP协议:可扩展消息处理协议,专为及时通信系统设计的通信协议,用于即时消息以及在线探测
  • Flash的XmlSocket:Flash Media Server 是一个强大的流媒体服务器,它基于RTMP协议,提供了稳定的流媒体交互功能,内置远程共享对象的机制,是浏览器创建并连接服务器的远程共享
  • websocket协议:WebSocket是一种在单个TCP连接上进行全双工通信的协议WebSocket使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在WebSocket
    API中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输

django实现websocket大致上有两种方式,一种channels,一种是dwebsocket。channels依赖于redis,twisted等


提示:以下是本篇文章正文内容,下面案例可供参考

一、Channels?

Channels安装与配置

pip install channels
pip channels_redis
pip install pypiwin32

settings配置

INSTALLED_APPS = [
'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'channels', # 添加channels功能
    'chat',
]
ASGI_APPLICATION = '项目名.routing.application'
CHANNEL_LAYERS = {
    'default': {
        'BACKEND': 'channels_redis.core.RedisChannelLayer',
        'CONFIG': {
            "hosts": [('127.0.0.1', 6379)],
        },
    },
}

说明:

  • CHANNEL_LAYERS用于设置Redis数据库的连接方式.
  • ASGI_APPLICATION:代表routing.py定义的application对象。

项目名文件夹下定义routing.py

from channels.routing import ProtocolTypeRouter
from channels.routing import URLRouter
from .urls import websocket_urlpatterns

application = ProtocolTypeRouter({
    # (http->django views is added by default)
    'websocket': AuthMiddlewareStack(
        URLRouter(
            websocket_urlpatterns
        )
    ),
})

说明:

定义这个文件是为了响应ASGI_APPLICATION定义application对象,将Django与该插件建立连接。

在项目名文件夹下urls.py,定义websocket_urlpatterns

from django.urls import path, include
from .consumers import ChatConsumer
urlpatterns = [
    path('', include(('chat.urls', 'chat'), namespace='chat'))
]

websocket_urlpatterns = [
    path('ws/chat//', ChatConsumer),
]

说明:

  • websocket_urlpatterns用于定义Channels的路由信息,上数定义的路由ws/chat//,它是由consumers.py的ChatConsumer处理和响应HTTP请求的,该路由作为Channels的API接口由网页的Javascript与该路由构建通信连接,使浏览器和服务器之间相互传递数据。

最后定义consumers.py

# from asgiref.sync import async_to_sync
# from channels.generic.websocket import WebsocketConsumer
# import json
#
# # 将消费者代码为同步。
# class ChatConsumer(WebsocketConsumer):
#     def connect(self):
#         self.room_name = self.scope['url_route']['kwargs']['room_name']
#         self.room_group_name = 'chat_%s' % self.room_name
#
#         # Join room group
#         async_to_sync(self.channel_layer.group_add)(
#             self.room_group_name,
#             self.channel_name
#         )
#
#         self.accept()
#
#     def disconnect(self, close_code):
#         # Leave room group
#         async_to_sync(self.channel_layer.group_discard)(
#             self.room_group_name,
#             self.channel_name
#         )
#
#     # Receive message from WebSocket
#     def receive(self, text_data):
#         text_data_json = json.loads(text_data)
#         message = text_data_json['message']
#
#         # Send message to room group
#         async_to_sync(self.channel_layer.group_send)(
#             self.room_group_name,
#             {
#                 'type': 'chat_message',
#                 'message': message
#             }
#         )
#
#     # Receive message from room group
#     def chat_message(self, event):
#         message = event['message']
#
#         # Send message to WebSocket
#         self.send(text_data=json.dumps({
#             'message': message
#         }))


#-------------low------------
# 将消费者代码重写为异步,以提高其性能。
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

        # 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):
        # 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):
        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
        }))

下面可以很简单的实现在线聊天功能

大致思路:在应用urls中设置两个路由:创建聊天和进入聊天室。在创建聊天室后即跳转到进入聊天室路由。进入聊天室路由网页设置:使网页与项目插件连接,设置接收和发送信息(也可以设置异常事件触发器)。下面步骤

chat urls

from django.urls import path
from .views import *

urlpatterns = [
    # 用于开启新的聊天室
    path('', newChat, name='newChat'),
    # 创建聊天室
    path('/', room, name='room'),
]
chat views
from django.shortcuts import render
# 用于创建或进入聊天室
def newChat(request):
    return render(request, 'chat.html', locals())

# 创建聊天室
def room(request, room_name):
    return render(request, 'room.html', locals())
chat.html(截取部分重要)
<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) {  // enter, return
                document.querySelector('#submit').click();
            }
        };

        document.querySelector('#submit').onclick = function(e) {
            var roomName = document.querySelector('#input').value;
            window.location.pathname = '/' + roomName + '/';
        };
    </script>
</body>
说明:
确定聊天室名称后,单击进入或者回车将触发javaScript脚本。

room.html

<body>
    <textarea id="chat-log" cols="50" rows="6"></textarea>
    <br/>
    <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) {  // enter, return
            document.querySelector('#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>
说明:
	var chatSocket部分为了网页与项目插件连接
	chatSocket.onmessage接收数据,并展示到文本框
	chatSocket.onclose关闭连接,当出现异常时触发
	document.querySelector发送数据
	querySelector将文本展示到网页textarea组件中。

你可能感兴趣的:(Django,django,python)