详解Django3中直接添加Websockets方式


Posted in Python onFebruary 12, 2020

现在Django 3.0附带了对ASGI的支持,将Websockets添加到Django应用中不需要任何额外的依赖关系。 在本文中,您将学习如何通过扩展默认的ASGI应用程序来使用Django处理Websocket。 我们将介绍如何在示例ASGI应用程序中处理Websocket连接,发送和接收数据以及实现业务逻辑。

入门

首先,您需要在计算机上安装Python> = 3.6。 Django 3.0仅与Python 3.6及更高版本兼容,因为它使用了async和await关键字。 完成Python版本设置后,创建一个项目目录并CD进入。 然后,将Django安装在virtualenv内,并在您的项目目录中创建一个新的Django应用:

$ mkdir django_websockets && cd django_websockets
$ python -m venv venv
$ source venv/bin/activate
$ pip install django
$ django-admin startproject websocket_app .

看一下Django应用程序的websocket_app目录。 您应该看到一个名为asgi.py的文件。 其内容如下所示:

import os
 
from django.core.asgi import get_asgi_application
 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'websocket_app.settings')
 
application = get_asgi_application()

该文件提供了默认的Django ASGI设置,并公开了一个名为application的ASGI应用程序,可以使用uvicorn或daphne等ASGI服务器运行该应用程序。 在进一步介绍之前,让我们看一下ASGI应用程序的结构。

ASGI应用程序结构

ASGI或“异步服务器网关接口”是用于使用Python构建异步Web服务的规范。它是WSGI的精神继承者,WSGI已被Django和Flask等框架使用了很长时间。 ASGI使您可以使用Python的本机异步/等待功能来构建支持长期连接的Web服务,例如Websockets和Server Sent Events。

ASGI应用程序是一个异步函数,它带有3个参数:作用域(当前请求的上下文),接收(一个异步函数,可让您侦听传入的事件)和发送(一个异步函数,可将事件发送至客户端)。

在ASGI应用程序内部,您可以根据范围字典中的值路由请求。例如,您可以通过检查scope [‘type']的值来检查该请求是HTTP请求还是Websocket请求。要侦听来自客户端的数据,您可以等待接收功能。准备好将数据发送到客户端时,可以等待发送功能,然后将要发送给客户端的任何数据传递给客户端。让我们看一下这在示例应用程序中是如何工作的。

创建一个ASGI应用

在我们的asgi.py文件中,我们将使用我们自己的ASGI应用程序包装Django的默认ASGI应用程序功能,以便自己处理Websocket连接。为此,我们需要定义一个名为application的异步函数,该函数需要3个ASGI参数:scope,receive和send。将get_asgi_application调用的结果重命名为django_application,因为我们需要它处理HTTP请求。在我们的应用程序函数内部,我们将检查scope [‘type']的值以确定请求类型。如果请求类型为“ http”,则该请求为普通的HTTP请求,我们应该让Django处理它。如果请求类型为“ websocket”,那么我们将自己处理逻辑。生成的asgi.py文件应如下所示:

import os
 
from django.core.asgi import get_asgi_application
 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'websocket_app.settings')
 
django_application = get_asgi_application()
 
async def application(scope, receive, send):
  if scope['type'] == 'http':
    # Let Django handle HTTP requests
    await django_application(scope, receive, send)
  elif scope['type'] == 'websocket':
    # We'll handle Websocket connections here
    pass
  else:
    raise NotImplementedError(f"Unknown scope type {scope['type']}")

现在,我们需要创建一个函数来处理Websocket连接。 在与asgi.py文件相同的文件夹中创建一个名为websocket.py的文件,并定义一个名为websocket_application的ASGI应用程序函数,该函数接受3个ASGI参数。 接下来,我们将在我们的asgi.py文件中导入websocket_application,并在我们的应用程序函数内部调用它来处理Websocket请求,传入范围,接收和发送参数。 它看起来应该像这样:

# asgi.py
import os
 
from django.core.asgi import get_asgi_application
from websocket_app.websocket import websocket_application
 
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'websocket_app.settings')
 
django_application = get_asgi_application()
 
async def application(scope, receive, send):
  if scope['type'] == 'http':
    await django_application(scope, receive, send)
  elif scope['type'] == 'websocket':
    await websocket_application(scope, receive, send)
  else:
    raise NotImplementedError(f"Unknown scope type {scope['type']}")
 
# websocket.py
async def websocket_application(scope, receive, send):
  pass

接下来,让我们为Websocket应用程序实现一些逻辑。我们将监听所有Websocket连接,当客户端发送字符串“ ping”时,我们将以字符串“ pong!”进行响应。

在websocket_application函数内部,我们将定义一个不确定的循环,该循环将处理Websocket请求,直到关闭连接。在该循环内,我们将等待服务器从客户端收到的任何新事件。然后,我们将根据事件的内容采取行动,并将响应发送给客户端。

首先,让我们处理连接。当新的Websocket客户端连接到服务器时,我们将收到“ websocket.connect”事件。为了允许这种连接,我们将发送一个“ websocket.accept”事件作为响应。这将完成Websocket握手并与客户端建立持久连接。

当客户端终止其与服务器的连接时,我们还需要处理断开连接事件。为此,我们将监听“ websocket.disconnect”事件。当客户端断开连接时,我们将摆脱不确定的循环。

最后,我们需要处理来自客户端的请求。为此,我们将监听“ websocket.receive”事件。当我们从客户端收到“ websocket.receive”事件时,我们将检查event [‘text']的值是否为“ ping”。如果是,我们将发送一个'websocket.send'事件,其文本值为'pong!'。

设置Websocket逻辑后,我们的websocket.py文件应如下所示:

# websocket.py
async def websocket_application(scope, receive, send):
  while True:
    event = await receive()
 
    if event['type'] == 'websocket.connect':
      await send({
        'type': 'websocket.accept'
      })
    
    if event['type'] == 'websocket.disconnect':
      break
    
    if event['type'] == 'websocket.receive':
      if event['text'] == 'ping':
        await send({
          'type': 'websocket.send',
          'text': 'pong!'
        })

测试

现在,我们的ASGI应用程序已设置为处理Websocket连接,并且我们已经实现了Websocket服务器逻辑,让我们对其进行测试。 目前,Django开发服务器不使用asgi.py文件,因此您将无法使用./manage.py runserver测试连接。 相反,您需要使用ASGI服务器(例如uvicorn)运行该应用程序。 让我们安装它:

$ pip install uvicorn

安装uvicorn后,我们可以使用以下命令运行ASGI应用程序:

$ uvicorn websocket_app.asgi:application
INFO:   Started server process [25557]
INFO:   Waiting for application startup.
INFO:   ASGI 'lifespan' protocol appears unsupported.
INFO:   Application startup complete.
INFO:   Uvicorn running on http://127.0.0.1:8000 (Press CTRL+C to quit)

要测试Websocket连接,请在新选项卡中打开浏览器的开发工具。 在控制台中,创建一个名为ws的新Websocket实例,该实例指向ws:// localhost:8000 /。 然后将onmessage处理程序附加到将event.data记录到控制台的ws。 最后,调用ws.send('ping')将消息发送到服务器。 您应该看到值“ pong!”。 登录到控制台。

> ws = new WebSocket('ws://localhost:8000/')
 WebSocket {url: "ws://localhost:8000/", readyState: 0, bufferedAmount: 0, onopen: null, onerror: null, …}
> ws.onmessage = event => console.log(event.data)
 event => console.log(event.data)
> ws.send("ping")
 undefined
 pong!

恭喜! 现在,您知道了如何使用ASGI将Websocket支持添加到Django应用程序中。 去用它来制作很棒的东西。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python使用reportlab画图示例(含中文汉字)
Dec 03 Python
python使用reportlab实现图片转换成pdf的方法
May 22 Python
Python编程实现双击更新所有已安装python模块的方法
Jun 05 Python
python僵尸进程产生的原因
Jul 21 Python
centos6.4下python3.6.1安装教程
Jul 21 Python
Python对数据进行插值和下采样的方法
Jul 03 Python
python生成requirements.txt的两种方法
Sep 18 Python
Python时间差中seconds和total_seconds的区别详解
Dec 26 Python
Pytorch 实现冻结指定卷积层的参数
Jan 06 Python
python用pip install时安装失败的一系列问题及解决方法
Feb 24 Python
python百行代码自制电脑端网速悬浮窗的实现
May 12 Python
python spilt()分隔字符串的实现示例
May 21 Python
Tensorflow 使用pb文件保存(恢复)模型计算图和参数实例详解
Feb 11 #Python
TensorFlow:将ckpt文件固化成pb文件教程
Feb 11 #Python
TensorFlow获取加载模型中的全部张量名称代码
Feb 11 #Python
tensorflow 获取checkpoint中的变量列表实例
Feb 11 #Python
python使用正则表达式去除中文文本多余空格,保留英文之间空格方法详解
Feb 11 #Python
python 函数中的参数类型
Feb 11 #Python
python正则过滤字母、中文、数字及特殊字符方法详解
Feb 11 #Python
You might like
php access 数据连接与读取保存编辑数据的实现代码
2010/05/12 PHP
php url路由入门实例
2014/04/23 PHP
CI框架中zip类应用示例
2014/06/17 PHP
Laravel 4 初级教程之安装及入门
2014/10/30 PHP
将FCKeditor导入PHP+SMARTY的实现方法
2015/01/15 PHP
一个非常完美的读写ini格式的PHP配置类分享
2015/02/12 PHP
PHP SPL 被遗落的宝石【SPL应用浅析】
2018/04/20 PHP
Thinkphp5.0框架的Db操作实例分析【连接、增删改查、链式操作等】
2019/10/11 PHP
IE中直接运行显示当前网页中的图片 推荐
2006/08/31 Javascript
漂亮的提示信息(带箭头)
2007/03/21 Javascript
js中判断文本框是否为空的两种方法
2011/07/31 Javascript
JQuery操作Select的Options的Bug(IE8兼容性视图模式)
2013/04/21 Javascript
BootstrapTable与KnockoutJS相结合实现增删改查功能【一】
2016/05/10 Javascript
BootStrap.css 在手机端滑动时右侧出现空白的原因及解决办法
2016/06/07 Javascript
jQuery实现点击后高亮背景固定显示的菜单效果【附demo源码下载】
2016/09/21 Javascript
jQuery点击弹出层弹出模态框点击模态框消失代码分享
2017/01/21 Javascript
canvas简单快速的实现知乎登录页背景效果
2017/05/08 Javascript
详解vue-cli与webpack结合如何处理静态资源
2017/09/19 Javascript
微信小程序实现即时通信聊天功能的实例代码
2018/08/17 Javascript
Bootstrap-table自定义可编辑每页显示记录数
2018/09/07 Javascript
vue中利用Promise封装jsonp并调取数据
2019/06/18 Javascript
在Vue环境下利用worker运行interval计时器的步骤
2019/08/01 Javascript
vue 使用localstorage实现面包屑的操作
2020/11/16 Javascript
详解JavaScript执行模型
2020/11/16 Javascript
Python中集合类型(set)学习小结
2015/01/28 Python
Python多线程编程(八):使用Event实现线程间通信
2015/04/05 Python
Django开发的简易留言板案例详解
2018/12/04 Python
Python 使用多属性来进行排序
2019/09/01 Python
Anthropologie英国:美国家喻户晓的休闲服装和家居产品品牌
2018/12/05 全球购物
Java模拟试题
2014/11/10 面试题
自我鉴定总结
2014/03/24 职场文书
教师党员一句话承诺
2014/03/28 职场文书
竞赛口号大全
2014/06/16 职场文书
私人房屋买卖协议书
2014/10/04 职场文书
教代会开幕词
2015/01/28 职场文书
工作失职自我检讨书
2015/05/05 职场文书