Tornado整合socketio(一)
liebian365 2024-11-26 05:52 16 浏览 0 评论
最近在做的项目中,需要将手机中的视频流或者音频流发送给服务端,再由服务端转发给浏览器端,起初我使用redis作为中转,将数据发到redis中,再由redis的发布订阅功能,整体架构如下
主要是利用了redis的pub/sub功能,这种方案也没有什么问题,但是整体的性能瓶颈受redis的影响。
最近接触到socketio,发现这种需求可以使用它来实现,但是网上查找了一些资料,在python的使用中,主要还是flask-socketio与原生的应用上,由于目前项目使用Tornado来构建,所以用了几天时间将socketio与Tornado的融合使用。
本教程会分几篇来介绍,主要以下几个章节
- socketio介绍与脚手架的搭建
- 定义消息处理事件
- 命名空间的使用
- 消息的发布与响应
- room的使用
- 前端vue中使用socketio与后端通信
一、socketio 简介
Socket.IO 支持实时、双向和基于事件的通信。它能够在任何平台、浏览器或设备上运行,可靠性和速度同样出色。它兼容websocket,在不支持websocket的设备上,会使用更加低层的长链接协议
Socket.io是一个WebSocket库,包括了客户端的js和服务器端的nodejs,它的目标是构建可以在不同浏览器和移动设备上使用的实时应用。它会自动根据浏览器从WebSocket、AJAX长轮询、Iframe流等等各种方式中选择最佳的方式来实现网络实时应用,非常方便和人性化,而且支持的浏览器最低达IE5.5
socket.io特点
- 实时分析:将数据推送到客户端,这些客户端会被表示为实时计数器,图表或日志客户。
- 实时通信和聊天:只需几行代码便可写成一个Socket.IO的”Hello,World”聊天应用。
- 二进制流传输:从1.0版本开始,Socket.IO支持任何形式的二进制文件传输,例如:图片,视频,音频等。
- 文档合并:允许多个用户同时编辑一个文档,并且能够看到每个用户做出的修改。
我这个项目主要利用其二进制流的传输。
二、python-socketio 简介
最初socketio的后台使用nodejs,后来又有了java,c++,python等后端的应用。
python的后端库地址 https://github.com/miguelgrinberg/python-socketio
但是注意,不同的版本并不兼容,参考下表
JavaScript Socket.IO version | Socket.IO protocol revision | Engine.IO protocol revision | python-socketio version |
0.9.x | 1, 2 | 1, 2 | Not supported |
1.x and 2.x | 3, 4 | 3 | 4.x |
3.x and 4.x | 5 | 4 | 5.x |
比如python-socketio用的是5.x的,那前端应该使用3.x或者4.x的socket.io 库,不同的版本不兼容。
另外,我演示时使用python版本是3.7.8的,我试过在3.5上的python中安装python-socketio时会安装失败,在安装一个依赖库didict时会失败
我的环境如下
python:3.7.8
python-socketio: 5.3.0
tornado: 6.0.4
windows x64
三、Tornado 的搭建
首先先使用Tornado构建一个简单的基础web应用
#-*- coding:utf-8 -*-
# author:Yang
# datetime:2021/5/19 23:26
# software: PyCharm
import os
import tornado.httpserver
import tornado.web
import tornado.gen
import tornado.concurrent
import tornado.autoreload
from tornado.platform.asyncio import AsyncIOMainLoop
import asyncio
import traceback
class Index(tornado.web.RequestHandler):
def get(self):
self.write("hello world")
class NotFount(tornado.web.RequestHandler):
def get(self):
self.write("404 not found")
def make_app(**kwargs):
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
debug=True,
)
return tornado.web.Application([
(r'/', Index),
(r'.*', NotFount),
], **settings, **kwargs)
if __name__ == '__main__':
try:
AsyncIOMainLoop().install()
app = make_app()
app.listen(8080)
loop = asyncio.get_event_loop()
processPoolNum = 2
try:
loop.run_forever()
except KeyboardInterrupt:
for task in asyncio.Task.all_tasks():
task.cancel()
loop.stop()
loop.run_forever()
finally:
loop.close()
except:
print(traceback.print_exc())
finally:
pass
上面的代码即可以搭建一个基础的Tornado web服务, 之后的代码就在这个基础上做添加。
四、添加python-socketio
python-socketio 分为server端与client端,在server端安装命令为
pip install python-socketio
先初始化socketio.AsyncServer 类的实例
import socketio
sio = socketio.AsyncServer(async_mode='tornado')
以下将python-socketio 简称为socketio
socketio server 有两个版本,一个同步的Server(),一个异步的AsyncServer() ,功能是样的,只是异步的server可以构建在asyncio环境中,由于我的Tornado应用也是使用asyncio,所以这里我使用了异步的server。
之后创建一个路由, 修改make_app函数
def make_app(**kwargs):
settings = dict(
template_path=os.path.join(os.path.dirname(__file__), "templates"),
static_path=os.path.join(os.path.dirname(__file__), "static"),
debug=True,
)
return tornado.web.Application([
(r'/', Index),
(r"/socket.io/", socketio.get_tornado_handler(sio)),
(r'.*', NotFount),
], **settings, **kwargs)
注意这里只能添加/socket.io/ 的路由,这里先这么写,之后在介绍client端面时再说明为什么。
还有一点要注意的,这个/socket.io/ 要定义在.* 路由之前,否则也会命中NotFount。
五、定义事件处理函数
当我们使用websocket时,我们会定义几个常用的方法,如connect 为成功连接上以后调用,disconnect 为断开连接时的操作。
对于服务端,同样我们也可以定义这些函数,我们称为事件(event), 定义事件有两种方式
1、使用sio.event装饰器@sio.event
def my_event(sid, data):
print("my_event get an message {} from {}".format(data, sid))
2、使用sio.on方法@sio.on("my event")
def event_special(sid, data):
print('special event get an message {} from {}'.format(data, sid))
两种方法定义的事件相同,使用第一个种方法,函数名即为事件名,这里就为my_event, 由于函数名不能有特殊的字符有空格,但是第二种方法,可以将事件名定义在装饰器参数中,如这里的my event 事件, 它中间有个空格,所以看需求,需要定义事件的名字中有特殊字符的需要使用on方法。
connect 和disconnect 事件是特殊的事件,在客户端进行连接和断开连接时自动调用
@sio.event
def connect(sid, environ, auth):
print('connect ', sid)
@sio.event
def disconnect(sid):
print('disconnect ', sid)
注意在自定义事件时,需要两个参数,一个是sid, 这个是客户端标识,一个是data, 这个是客户端发送过来的数据。
六、使用客户端进行连接
客户端的安装与服务端有所不同,使用pip install "python-socketio[client]" 安装同步版本
使用pip install "python-socketio[asyncio_client]" 安装异步版本,我这里使用同步版本的客户端,如果你需要在asyncio中使用则需要安装异步版本
import socketio
sio = socketio.Client()
sio.connect('http://localhost:8080')
三行代码即可进行连接web服务,并与之建立长连接,这里有一点要注意,我们在server端是创建了一个路由,
(r"/socket.io/", socketio.get_tornado_handler(sio))
但是这里连接的时候却不能将/socket.io/ 添加到connect的url中,只能写到根url
查看connect函数定义
def connect(self, url, headers={}, auth=None, transports=None,
namespaces=None, socketio_path='socket.io', wait=True,
wait_timeout=1):
这里有一个socketio_path 参数,也正是这个参数,所以在定义server端的时候,要加上那么一条路由,当然这个参数也可以自己定义,这里为了简单就不自定义了,只要知道这里是可以自己定义的。
调用client,观察server端的输出,当有客户端连接到服务端以后,服务端会自动触发connect事件,这里就执行了自定义的函数
@sio.event
def connect(sid, environ, auth):
print('connect ', sid)
打印输出
connect VUke1-fDf8dYFAyEAAAB, 其中后面的为客户端的sid, 当关闭客户端时,又会触发disconnect事件,输出disconnect VUke1-fDf8dYFAyEAAAB。
七、定义客户端事件
其实对于这种长连接的方式,客户端与服务端的界线已经有些模糊了,服务端也可以向客户端发送数据请求,这里的客户端也就相关于服务端。
我们在客户端定义一个connect事件, 用于连接上服务端以后执行的函数
@sio.event
def connect():
print("I'm connected!")
这里再次连接服务端,当连接成功以后,就会打印出 I'm connected!
八、客户端发送自定义事件
我们在服务端定义了两个事件 my_event 和my event, 那么客户端如何发送这两个事件呢?
客户端可以使用emit 函数
sio.connect('http://localhost:8080')
sio.emit("my event", {"data": "hello server"})
这行代码即可向服务端发送my event 事件,再将观察服务端的输出,
触发了my event 函数
@sio.on("my event")
def event_special(sid, data):
print('special event get an message {} from {}'.format(data, sid))
得到输出:
special event get an message {'data': 'hello server'} from kh-Ui9gCLG9b5jD4AAAF
九、服务端向客户端发送事件
客户端可以向服务端发送事件,服务端也可以向客户端发送事件,我们先在客户端定义一个事件
@sio.on("client event")
def client_event(data):
print("get server message:{}".format(data))
修改服务端my event事件代码
@sio.on("my event")
async def event_special(sid, data):
print('special event get an message {} from {}'.format(data, sid))
await sio.emit("client event", "hello {}".format(sid))
当服务端接收到my event事件以后,再向客户端发送一个client event事件,注意这里由于服务端使用的是异步的,所以这里要将函数改为async def, emit函数也需要使用await 。
再将调用客户端进行连接,这时客户端会得到如下输出
I'm connected!
get server message:hello tNp1Yb7OONZn2FwxAAAB
I'm connected! 是触发了connect事件,get server message:hello tNp1Yb7OONZn2FwxAAAB 是触发了client event 事件。
十、自动重连
当我们把服务端关掉以后,此时客户端的脚本并没有退出,当再将启动服务端的时候,客户端可以自动连接上,这个也是该库的方便之处,如果要自己写长连接的话,还要考虑重连问题。
到此,已经掌握了socketio与Tornado的最基本的使用,可以相互发送消息,之后的文章里会介绍更加详细命名空间,与web前端的交互操作。
参考
python-socketio官方文档
相关推荐
- 4万多吨豪华游轮遇险 竟是因为这个原因……
-
(观察者网讯)4.7万吨豪华游轮搁浅,竟是因为油量太低?据观察者网此前报道,挪威游轮“维京天空”号上周六(23日)在挪威近海发生引擎故障搁浅。船上载有1300多人,其中28人受伤住院。经过数天的调...
- “菜鸟黑客”必用兵器之“渗透测试篇二”
-
"菜鸟黑客"必用兵器之"渗透测试篇二"上篇文章主要针对伙伴们对"渗透测试"应该如何学习?"渗透测试"的基本流程?本篇文章继续上次的分享,接着介绍一下黑客们常用的渗透测试工具有哪些?以及用实验环境让大家...
- 科幻春晚丨《震动羽翼说“Hello”》两万年星间飞行,探测器对地球的最终告白
-
作者|藤井太洋译者|祝力新【编者按】2021年科幻春晚的最后一篇小说,来自大家喜爱的日本科幻作家藤井太洋。小说将视角放在一颗太空探测器上,延续了他一贯的浪漫风格。...
- 麦子陪你做作业(二):KEGG通路数据库的正确打开姿势
-
作者:麦子KEGG是通路数据库中最庞大的,涵盖基因组网络信息,主要注释基因的功能和调控关系。当我们选到了合适的候选分子,单变量研究也已做完,接着研究机制的时便可使用到它。你需要了解你的分子目前已有哪些...
- 知存科技王绍迪:突破存储墙瓶颈,详解存算一体架构优势
-
智东西(公众号:zhidxcom)编辑|韦世玮智东西6月5日消息,近日,在落幕不久的GTIC2021嵌入式AI创新峰会上,知存科技CEO王绍迪博士以《存算一体AI芯片:AIoT设备的算力新选择》...
- 每日新闻播报(September 14)_每日新闻播报英文
-
AnOscarstatuestandscoveredwithplasticduringpreparationsleadinguptothe87thAcademyAward...
- 香港新巴城巴开放实时到站数据 供科技界研发使用
-
中新网3月22日电据香港《明报》报道,香港特区政府致力推动智慧城市,鼓励公私营机构开放数据,以便科技界研发使用。香港运输署21日与新巴及城巴(两巴)公司签署谅解备忘录,两巴将于2019年第3季度,开...
- 5款不容错过的APP: Red Bull Alert,Flipagram,WifiMapper
-
本周有不少非常出色的app推出,鸵鸟电台做了一个小合集。亮相本周榜单的有WifiMapper's安卓版的app,其中包含了RedBull的一款新型闹钟,还有一款可爱的怪物主题益智游戏。一起来看看我...
- Qt动画效果展示_qt显示图片
-
今天在这篇博文中,主要实践Qt动画,做一个实例来讲解Qt动画使用,其界面如下图所示(由于没有录制为gif动画图片,所以请各位下载查看效果):该程序使用应用程序单窗口,主窗口继承于QMainWindow...
- 如何从0到1设计实现一门自己的脚本语言
-
作者:dong...
- 三年级语文上册 仿写句子 需要的直接下载打印吧
-
描写秋天的好句好段1.秋天来了,山野变成了美丽的图画。苹果露出红红的脸庞,梨树挂起金黄的灯笼,高粱举起了燃烧的火把。大雁在天空一会儿写“人”字,一会儿写“一”字。2.花园里,菊花争奇斗艳,红的似火,粉...
- C++|那些一看就很简洁、优雅、经典的小代码段
-
目录0等概率随机洗牌:1大小写转换2字符串复制...
- 二年级上册语文必考句子仿写,家长打印,孩子照着练
-
二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...
你 发表评论:
欢迎- 一周热门
- 最近发表
- 标签列表
-
- wireshark怎么抓包 (75)
- qt sleep (64)
- cs1.6指令代码大全 (55)
- factory-method (60)
- sqlite3_bind_blob (52)
- hibernate update (63)
- c++ base64 (70)
- nc 命令 (52)
- wm_close (51)
- epollin (51)
- sqlca.sqlcode (57)
- lua ipairs (60)
- tv_usec (64)
- 命令行进入文件夹 (53)
- postgresql array (57)
- statfs函数 (57)
- .project文件 (54)
- lua require (56)
- for_each (67)
- c#工厂模式 (57)
- wxsqlite3 (66)
- dmesg -c (58)
- fopen参数 (53)
- tar -zxvf -c (55)
- 速递查询 (52)