目录

前言

不想看我瞎BB可以直接跳到这里

1.WebSocket

1.1 ajax轮询

1.2 long poll

1.3 Websocket

2.Channels

2.1 WSGI

2.2 ASGI

3.准备Django环境

3.1 安装channels

3.2 创建一个Django项目

3.3 创建一个聊天应用程序

3.4 添加索引视图

3.5 在chat/views.py中创建视图函数:

3.6 在chat中创建一个名为urls.py用来存放路由:

3.7 在mysite/urls.py中配置总路由

3.8 启动一下,检查

4. channels在Django中的集成

4.1 配置环境

4.3 在chat/views.py创建视图函数

4.4 在chat/urls.py 创建路由

4.5 WebSocket 连接配置

4.6 配置socket路由

4.7 启用通道层

4.7.1 安装 channels_redis

4.7.2 配置通道层的环境

4.7.3 测试一下环境是否配置成功

4.8 替换旧代码

4.9 测试

5. 源码参考


前言

网上绝大多数博客都是发送端或者接收端同时作为服务器,这不扯么…  要不就是写的乱七八糟的根本运行不了,实在受不了,经过一段时间的学习,决定自己写一份保姆级别的集成文章,文末附带源码

不想看我瞎BB可以直接跳到这里

为了实现双向奔赴通信,我选择websocket;但是Django 3.0往上走就不支持websocket了就很无语,所以这里我通过channels实现websocket。

1.WebSocket

在讲Websocket之前,先了解下 long pollajax轮询 的原理。

1.1 ajax轮询

ajax轮询的原理非常简单,让浏览器隔个几秒就发送一次请求,询问服务器是否有新信息。这样就很浪费服务器资源,项目小还好,项目要是大一点老板直接让你走人。

1.2 long poll

  long poll 其实原理跟 ajax轮询 差不多,都是采用轮询的方式,不过采取的是阻塞模型(一直打电话,没收到就不挂电话),也就是说,客户端发起连接后,如果没消息,就一直不返回Response给客户端。直到有消息才返回,返回完之后,客户端再次建立连接,周而复始。

  ajax轮询 需要服务器有很快的处理速度和资源(速度)。long poll 需要有很高的并发,也就是说同时接待客户的能力(场地大小)。

1.3 Websocket

  WebSocket是一种在单个TCP连接上进行全双工通讯的协议。WebSocket允许服务端主动向客户端推送数据。在WebSocket协议中,客户端浏览器和服务器只需要完成一次握手就可以创建持久性的连接,并在浏览器和服务器之间进行双向的数据传输。

2.Channels

  Django本身不支持WebSocket,但可以通过集成Channels框架来实现WebSocket

  Channels是针对Django项目的一个增强框架,可以使Django不仅支持HTTP协议,还能支持WebSocketMQTT等多种协议,同时Channels还整合了Djangoauth以及session系统方便进行用户管理及认证。

2.1 WSGI

WSGI(Python Web Server Gateway Interface):为Python语言定义的Web服务器和Web应用程序或者框架之间的一种简单而通用的接口。

2.2 ASGI

ASGI(Asynchronous Web Server Gateway Interface):异步网关协议接口,一个介于网络协议服务和Python应用之间的标准接口,能够处理多种通用的协议类型,包括HTTPHTTP2WebSocket

WSGI是基于HTTP协议模式的,不支持WebSocket,而ASGI的诞生则是为了解决Python常用的WSGI不支持当前Web开发中的一些新的协议标准。同时,ASGI对于WSGI原有的模式的支持和WebSocket的扩展,即ASGIWSGI的扩展。

3.准备Django环境

本文是为 Channels 3.0 编写的,它支持 Python 3.6+Django 2.2+

python3 -m django --version

3.1 安装channels

pip install channels

3.2 创建一个Django项目

3.3 创建一个聊天应用程序

python manage.py startapp chat

我们需要告诉我们的项目 chat应用程序已安装。编辑 mysite/settings.py文件并添加'chat'INSTALLED_APPS设置。它看起来像这样:

3.4 添加索引视图

我们现在将创建第一个视图,一个索引视图,允许您键入要加入的聊天室的名称。

templates目录中创建一个名为chat的文件夹。用来存放html页面。

index.html

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/><title>Chat Rooms</title>
</head>
<body>What chat room would you like to enter?<br><input id="room-name-input" type="text" size="100"><br><input id="room-name-submit" type="button" value="Enter"><script>document.querySelector('#room-name-input').focus();document.querySelector('#room-name-input').onkeyup = function(e) {if (e.keyCode === 13) {  // enter, returndocument.querySelector('#room-name-submit').click();}};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.5 在chat/views.py中创建视图函数:

# chat/views.py
from django.shortcuts import renderdef index(request):return render(request, 'chat/index.html')

3.6 在chat中创建一个名为urls.py用来存放路由

# chat/urls.py
from django.urls import pathfrom . import viewsurlpatterns = [path('', views.index, name='index'),
]

3.7 在mysite/urls.py中配置总路由

# mysite/urls.py
from django.conf.urls import include
from django.urls import path
from django.contrib import adminurlpatterns = [path('chat/', include('chat.urls')),path('admin/', admin.site.urls),
]

3.8 启动一下,检查

Django 准备成功!

4. channels在Django中的集成

到目前为止,我们刚刚创建了一个常规的 Django 应用程序;我们根本没有使用过 Channels 库。现在是整合的时候了。

我从为 Channels 创建一个根路由配置开始。Channels路由配置是一个类似于 Django URLconf 的 ASGI 应用程序,因为它告诉 Channels 当 Channels 服务器接收到 HTTP 请求时要运行什么代码。

4.1 配置环境

mysite/asgi.py文件下

没有这个python文件可以自己手动创建一个

# mysite/asgi.py
import osfrom channels.routing import ProtocolTypeRouter
from django.core.asgi import get_asgi_applicationos.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')application = ProtocolTypeRouter({"http": get_asgi_application(),# Just HTTP for now. (We can add other protocols later.)
})

注意:

Django 2.2 没有内置的 ASGI 支持,所以我们需要使用 Channel 的后备替代方案。mysite/asgi.py像这样创建:

# mysite/asgi.py
import osimport django
from channels.http import AsgiHandler
from channels.routing import ProtocolTypeRouteros.environ.setdefault('DJANGO_SETTINGS_MODULE', 'mysite.settings')
django.setup()application = ProtocolTypeRouter({"http": AsgiHandler(),# Just HTTP for now. (We can add other protocols later.)
})

现在将频道库添加到已安装的应用程序列表中。编辑mysite/settings.py文件并添加'channels'到 INSTALLED_APPS设置中,还需要将 Channels 指向根路由配置

全新启动一下,下面这种就算配置成功。

4.2 添加视图

在templates/chat里面新增一个html页面room.html

<!DOCTYPE html>
<html>
<head><meta charset="utf-8"/><title>Chat Room</title>
</head>
<body><textarea id="chat-log" cols="100" rows="20"></textarea><br><input id="chat-message-input" type="text" size="100"><br><input id="chat-message-submit" type="button" value="Send">{{ room_name|json_script:"room-name" }}<script>const roomName = JSON.parse(document.getElementById('room-name').textContent);const chatSocket = new WebSocket('ws://'+ window.location.host+ '/ws/chat/'+ roomName+ '/');chatSocket.onmessage = function(e) {const data = JSON.parse(e.data);document.querySelector('#chat-log').value += (data.message + '\n');};chatSocket.onclose = function(e) {console.error('Chat socket closed unexpectedly');};document.querySelector('#chat-message-input').focus();document.querySelector('#chat-message-input').onkeyup = function(e) {if (e.keyCode === 13) {  // enter, returndocument.querySelector('#chat-message-submit').click();}};document.querySelector('#chat-message-submit').onclick = function(e) {const messageInputDom = document.querySelector('#chat-message-input');const message = messageInputDom.value;chatSocket.send(JSON.stringify({'message': message}));messageInputDom.value = '';};</script>
</body>
</html>

4.3 在chat/views.py创建视图函数

# chat/views.py
from django.shortcuts import renderdef index(request):return render(request, 'chat/index.html', {})def room(request, room_name):return render(request, 'chat/room.html', {'room_name': room_name})

4.4 在chat/urls.py 创建路由

# chat/urls.py
from django.urls import pathfrom . import viewsurlpatterns = [path('', views.index, name='index'),path('<str:room_name>/', views.room, name='room'),
]

4.5 WebSocket 连接配置

注意:

使用公共路径前缀(例如/ws/将 WebSocket 连接与普通 HTTP 连接区分开来)是一种很好的做法,因为它会使在某些配置中更容易地将 Channels 部署到生产环境。

创建一个新文件chat/consumers.py

将以下代码写入chat/consumers.py

# chat/consumers.py
import json
from channels.generic.websocket import WebsocketConsumerclass ChatConsumer(WebsocketConsumer):def connect(self):self.accept()def disconnect(self, close_code):passdef receive(self, text_data):text_data_json = json.loads(text_data)message = text_data_json['message']self.send(text_data=json.dumps({'message': message}))

4.6 配置socket路由

现在还需要为chat具有到消费者的路由的应用程序创建路由配置

创建一个新文件chat/routing.py

将以下代码写入chat/routing.py

# chat/routing.py
from django.urls import re_pathfrom . import consumerswebsocket_urlpatterns = [re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]

调用as_asgi()类方法是为了获得一个 ASGI 应用程序,该应用程序将为每个用户连接实例化我们的消费者实例。这类似于 Django 的as_view(),它对每个请求的 Django 视图实例起到相同的作用。

下一步是将根路由配置指向 chat.routing模块

# mysite/asgi.py
import osfrom channels.auth import AuthMiddlewareStack
from channels.routing import ProtocolTypeRouter, URLRouter
from django.core.asgi import get_asgi_application
import chat.routingos.environ.setdefault("DJANGO_SETTINGS_MODULE", "mysite.settings")application = ProtocolTypeRouter({"http": get_asgi_application(),"websocket": AuthMiddlewareStack(URLRouter(chat.routing.websocket_urlpatterns)),
})

这个时候就已经配置好了运行之后发现根本聊天不起来!!

这不就完了吗。。。。。。。

解决办法:

4.7 启用通道层

我们将使用一个使用 Redis 作为其后备存储的通道层。要启动 Redis 服务器

要是启动redis出错请看我另外一篇文章:windows下Redis server启动一闪而过的解决方案

4.7.1 安装 channels_redis

以便 Channels 知道如何与 Redis 交互

pip install channels_redis

4.7.2 配置通道层的环境

mysite/settings.py文件里面并CHANNEL_LAYERS在底部添加一个设置

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

4.7.3 测试一下环境是否配置成功

import channels.layers
channel_layer = channels.layers.get_channel_layer()
from asgiref.sync import async_to_sync
async_to_sync(channel_layer.send)('test_channel', {'type': 'hello'})
async_to_sync(channel_layer.receive)('test_channel')#结果:
{'type': 'hello'}

4.8 替换旧代码

现在我们有了一个通道层,在/chat/consumer. 将以下代码写入chat/consumers.py,替换旧代码

# chat/consumers.py
import json
from asgiref.sync import async_to_sync
from channels.generic.websocket import WebsocketConsumerclass 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 groupasync_to_sync(self.channel_layer.group_add)(self.room_group_name,self.channel_name)self.accept()def disconnect(self, close_code):# Leave room groupasync_to_sync(self.channel_layer.group_discard)(self.room_group_name,self.channel_name)# Receive message from WebSocketdef receive(self, text_data):text_data_json = json.loads(text_data)message = text_data_json['message']# Send message to room groupasync_to_sync(self.channel_layer.group_send)(self.room_group_name,{'type': 'chat_message','message': message})# Receive message from room groupdef chat_message(self, event):message = event['message']# Send message to WebSocketself.send(text_data=json.dumps({'message': message}))

当用户发布消息时,JavaScript 函数将通过 WebSocket 将消息传输到 ChatConsumer。ChatConsumer将收到该消息并将其转发到与房间名称对应的组。同一组中(因此在同一房间中)的每个 ChatConsumer 都会收到来自该组的消息,并通过 WebSocket 将其转发回 JavaScript,然后将其附加到聊天日志中,从而实现聊天功能。

ChatConsumer代码的几个部分进一步的解释:

  • self.scope['url_route']['kwargs']['room_name']

    • 'room_name'chat/routing.py 打开到消费者的 WebSocket 连接的 URL 路由中获取参数。
    • 每个使用者都有一个范围,其中包含有关其连接的信息,特别是包括来自 URL 路由的任何位置或关键字参数以及当前经过身份验证的用户(如果有)。
  • self.room_group_name = 'chat_%s' % self.room_name

    • 直接从用户指定的房间名称构造一个 Channels 组名称,没有任何引用或转义。
    • 组名只能包含字母、数字、连字符和句点。因此,此示例代码将在包含其他字符的房间名称上失败。
  • async_to_sync(self.channel_layer.group_add)(...)

    • 加入一个小组。
    • 需要 async_to_sync(...) 包装器,因为 ChatConsumer 是一个同步 WebsocketConsumer,但它正在调用异步通道层方法。(所有通道层方法都是异步的。)
    • 组名称仅限于 ASCII 字母数字、连字符和句点。由于此代码直接从房间名称构造一个组名称,如果房间名称包含任何在组名称中无效的字符,它将失败。
  • self.accept()

    • 接受 WebSocket 连接。
    • 如果您没有在 connect() 方法中调用 accept(),则连接将被拒绝并关闭。例如,您可能希望拒绝连接,因为请求用户无权执行请求的操作。
    • 如果您选择接受连接,建议将 accept() 作为connect() 中的最后一个操作调用。
  • async_to_sync(self.channel_layer.group_discard)(...)

    • 离开一组。
  • async_to_sync(self.channel_layer.group_send)

    • 向组发送事件。
    • 一个事件有一个特殊的'type'键,对应于应该在接收事件的消费者上调用的方法的名称。

4.9 测试

到这里已经完成了简单的channels在Django中的集成。

下一篇文章将会说到Django集成channels实现socket 摄像头实时视频传输

5. 源码参考

码云:Django集成channels: Django集成channels系列

保姆级别 附带源码 Django集成channels(一)实现简单聊天功能相关推荐

  1. java天眼培训_Java天眼大型分布式跟踪系统 附带源码_IT教程网

    资源名称:Java天眼大型分布式跟踪系统 附带源码 教程内容: APP: 接入skyeye-client的系统会通过kafkaAppender向kafka写入日志 es-indexer-group: ...

  2. 分布式跟踪系统 java_Java天眼大型分布式跟踪系统 附带源码

    资源名称:Java天眼大型分布式跟踪系统 附带源码 教程内容: APP: 接入skyeye-client的系统会通过kafkaAppender向kafka写入日志 es-indexer-group: ...

  3. 入门机器学习?好好看看《智能问答与深度学习》随书附带源码

    目录 <智能问答与深度学习>随书附带源码 安装依赖软件 下载源码 执行示例程序 取得帮助 第二章 机器学习基础 第三章 自然语言处理基础 第四章 深度学习初步 第五章 词向量实现及应用 第 ...

  4. SpringMVC异常处理机制详解[附带源码分析]

    SpringMVC异常处理机制详解[附带源码分析] 参考文章: (1)SpringMVC异常处理机制详解[附带源码分析] (2)https://www.cnblogs.com/fangjian0423 ...

  5. android类中定义颜色,自定义实现简单的Android颜色选择器(附带源码)

    在写Android App过程中需要一个简单的颜色选择器,Android自带的ColorPicker和网上的一些ColorPicker都太高端了,都实现了颜色渐变功能,我要的不需要那么复杂,只想提供几 ...

  6. SpringMVC关于json、xml自动转换的原理研究[附带源码分析 --转

    SpringMVC关于json.xml自动转换的原理研究[附带源码分析] 原文地址:http://www.cnblogs.com/fangjian0423/p/springMVC-xml-json-c ...

  7. python面试题及答案bt_公布上期Python笔试题答案,附带源码与运行结果

    今天发布的内容没有废话,就是上一期的笔试题答案,由于内容较多,我们今天就公布前五道题的答案,附带源码哦!请感兴趣的读者细细研究! 笔试 笔试题一答案:利用Python创建如图所示的二叉树,并给出前序. ...

  8. Dx11DemoBase 基类(三) 实例应用 【已实现】【附带源码】

    现在我已经到哪了? 读书时,尤其是技术知识书籍, 我一般会担忧自己是否陷得太深, 细节关注得太多, 而忘了整体的过程: 一直以来对Direct3D 很畏惧, 因为太多函数和细节:现在我必须暂缓下, 看 ...

  9. app商城源码_海量的SpringBoot和SSM项目【附带源码+视频教程】快速成为全栈

    为了帮助更多的小伙伴进行项目的锻炼,孟哥整理较多的实战项目,包括SSM.Springboot.Springcloud.小程序等. 各种项目还在不断的更新中--仅限制学习使用,若有侵权,请联系删除. 点 ...

最新文章

  1. 学习用C#在Unity中创建一个2D Metroidvania游戏
  2. Simple Dynamic Strings(SDS)源码解析和使用说明二
  3. WPF INotifyPropertyChanged 通过特性减少代码量
  4. 坑中速记整理! 使用 kotlin 写第一个 ReactNative Android 模块
  5. AX 2009 时间类型函数操作
  6. 大数据学习笔记1000条
  7. 用 Java 爬小姐姐图片,这个厉害了。。。
  8. RESTful设计原则和样例(开发前后台接口)
  9. 编写数据访问代码测试–单元测试是浪费
  10. 了不起的华人女数学家们
  11. win7安装python
  12. 最末参与者优化 lpo_优化博客以提高读者参与度的6种方法
  13. 三天内出现多次晃动,华强北最高楼今日起已被封闭了
  14. linux eclipse c++ 如何生成arm可执行文件_干货 | protobuf-c之嵌入式平台使用
  15. js日期时间控件------layDate
  16. 多方安全计算、联邦学习、可信计算 对比区别
  17. 坐标转换-换带计算(附软件下载)
  18. android图标分组名称唯美,手机屏幕分组好听名字
  19. 网易云系列爬虫-采集歌单内歌曲热评
  20. 这个开源好用的数据库建模工具,让我眼前一亮

热门文章

  1. 机器学习-机器学习基本方法
  2. wpf dataGrid 实现单行某个数据变化 ui 界面随之响应
  3. 安全编排自动化与响应 (SOAR) 技术解析
  4. 中国网游新十年:或将进入大制作时代
  5. FastReport VCL 2022
  6. 【题解】自己口胡的一道题目
  7. Listener Filter
  8. Android 逆向开发-apktool.yml的解析、修改、合并的java实现
  9. 使用360加固app,打多渠道包
  10. 接口对接实在太难了!