详解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实现根据IP地址和子网掩码算出网段的方法
Jul 30 Python
PyQt5实现无边框窗口的标题拖动和窗口缩放
Apr 19 Python
Python简单读写Xls格式文档的方法示例
Aug 17 Python
运用Python的webbrowser实现定时打开特定网页
Feb 21 Python
python 实现在一张图中绘制一个小的子图方法
Jul 07 Python
python对矩阵进行转置的2种处理方法
Jul 17 Python
python批量解压zip文件的方法
Aug 20 Python
Python读写文件模式和文件对象方法实例详解
Sep 17 Python
使用Python完成15位18位身份证的互转功能
Nov 06 Python
python用WxPython库实现无边框窗体和透明窗体实现方法详解
Feb 21 Python
python利用opencv保存、播放视频
Nov 02 Python
Django实现翻页的示例代码
May 24 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之第五天
2006/10/09 PHP
php提示Call-time pass-by-reference has been deprecated in的解决方法[已测]
2012/05/06 PHP
PHP函数getenv简介和使用实例
2014/05/12 PHP
form自动提交实例讲解
2017/07/10 PHP
Laravel6.2中用于用户登录的新密码确认流程详解
2019/10/16 PHP
laravel框架如何设置公共头和公共尾
2019/10/22 PHP
js中的内部属性与delete操作符介绍
2015/08/10 Javascript
谷歌showModalDialog()方法不兼容出现对话窗口的解决办法
2016/02/15 Javascript
KnockoutJS 3.X API 第四章之数据控制流foreach绑定
2016/10/10 Javascript
jquery中用jsonp实现搜索框功能
2016/10/18 Javascript
JavaScript利用闭包实现模块化
2017/01/13 Javascript
node.js部署之启动后台运行forever的方法
2018/05/23 Javascript
angular2 组件之间通过service互相传递的实例
2018/09/30 Javascript
在js代码拼接dom对象到页面上的模板总结
2018/10/21 Javascript
基于vue实现圆形菜单栏组件
2019/07/05 Javascript
解决Angularjs异步操作后台请求用$q.all排列先后顺序问题
2019/11/29 Javascript
如何使用vue slot创建一个模态框的实例代码
2020/05/24 Javascript
ant design vue 表格table 默认勾选几项的操作
2020/10/31 Javascript
[00:17]DOTA2荣耀之路5:It’s a disastah!
2018/05/28 DOTA
python中解析json格式文件的方法示例
2017/05/03 Python
Python实现导出数据生成excel报表的方法示例
2017/07/12 Python
python使用邻接矩阵构造图代码示例
2017/11/10 Python
python assert的用处示例详解
2019/04/01 Python
python爬虫 urllib模块反爬虫机制UA详解
2019/08/20 Python
浅谈python处理json和redis hash的坑
2020/07/16 Python
Sneaker Studio匈牙利:购买运动鞋
2018/03/26 全球购物
DJI大疆德国官方商城:大疆无人机
2018/09/01 全球购物
办公室副主任岗位职责
2013/11/25 职场文书
会计系毕业生求职信
2014/05/28 职场文书
大一新生期末自我评价
2014/09/12 职场文书
2015年档案管理员工作总结
2015/05/13 职场文书
致运动员赞词
2015/07/22 职场文书
2016年社区创先争优活动总结
2016/04/05 职场文书
2020年元旦祝福语录,总有适合你的
2019/12/31 职场文书
python中Matplotlib绘制直线的实例代码
2021/07/04 Python
如何解决php-fpm启动不了问题
2021/11/17 PHP