Tornado整合socketio(一)_tornado handler
haoteby 2025-09-11 01:08 195 浏览
最近在做的项目中,需要将手机中的视频流或者音频流发送给服务端,再由服务端转发给浏览器端,起初我使用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官方文档
相关推荐
- 如何随时清理浏览器缓存_清理浏览器缓存怎么弄
-
想随时清理浏览器缓存吗?Cookieformac版是Macos上一款浏览器缓存清理工具,所有的浏览器Cookie,本地存储数据,HTML5数据库,FlashCookie,Silverlight,...
- Luminati代理动态IP教程指南配置代理VMLogin中文版反指纹浏览器
-
介绍如何使用在VMLogin中文版设置Luminati代理。首先下载VMLogin中文版反指纹浏览器(https://cn.vmlogin.com)对于刚接触Luminati动态ip的朋友,是不是不懂...
- mac清除工具分享,解除您在安全方面的后顾之忧
-
想要永久的安全的处理掉重要数据,删除是之一,使用今天小编分享的mac清除工具,为您的操作再增一层“保护”,小伙伴慎用哟,一旦使用就不可以恢复咯,来吧一起看看吧~mac清除工具分享,解除您在安全方面的后...
- 取代cookie的网站追踪技术:”帆布指纹识别”
-
【前言】一般情况下,网站或者广告联盟都会非常想要一种技术方式可以在网络上精确定位到每一个个体,这样可以通过收集这些个体的数据,通过分析后更加精准的去推送广告(精准化营销)或其他有针对性的一些活动。Co...
- 辅助上网为啥会被抛弃 曲奇(Cookie)虽甜但有毒
-
近期有个小新闻,大概很多小伙伴都没有注意到,那就是谷歌Chrome浏览器要弃用Cookie了!说到Cookie功能,很多小伙伴大概觉得不怎么熟悉,有可能还不如前一段时间被弃用的Flash“出名”,但它...
- 浏览器指纹是什么?浏览器指纹包括哪些信息
-
本文关键词:浏览器指纹、指纹浏览器、浏览器指纹信息、指纹浏览器原理什么是浏览器指纹?浏览器指纹是指浏览器的各种信息,当我们访问其他网站时,即使是在匿名的模式下,这些信息也可以帮助网站识别我们的身份。...
- 那些通用清除软件不曾注意的秘密_清理不常用的应用软件
-
系统清理就像卫生检查前的大扫除,即使你使出吃奶的劲儿把一切可能的地方都打扫过,还会留下边边角角的遗漏。随着大家电脑安全意识的提高,越来越多的朋友开始关注自己的电脑安全,也知道安装360系列软件来"武装...
- 「网络安全宣传周」这些安全上网小知识你要知道!
-
小布说:互联网改变了人们的衣食住行,但与之伴生的网络安全威胁也不容忽视。近些年来,风靡全球的勒索病毒、时有发生的电信诈骗、防不胜防的个人信息泄露时时刻刻都威胁着我们的生活。9月18日-24日是第四届...
- TypeScript 终极初学者指南_typescript 进阶
-
在过去的几年里TypeScript变得越来越流行,现在许多工作都要求开发人员了解TypeScript...
- jQuery知识一览_jquery的认识和使用
-
一、概览jQuery官网:https://jquery.com/jQuery是一个高效、轻量并且功能丰富的js库。核心在于查询query。...
- 我的第一个Electron应用_electronmy
-
hello,好久不见,最近笔者花了几天时间入门Electron,然后做了一个非常简单的应用,本文就来给各位分享一下过程,Electron大佬请随意~笔者开源了一个Web思维导图,虽然借助showSav...
- HTML5 之拖放(Drag 和 Drop)_html拖放api
-
简介拖放是一种常见的特性,即抓取对象以后拖到另一个位置。在HTML5中,拖放是标准的一部分,任何元素都能够拖放。先点击一个小例子:在用户开始拖动<p>元素时执行JavaScrip...
- 如何用JavaScript判断输入值是数字还是字母?
-
在日常开发中,我们有时候需要判断用户输入的是数字还是字母。本文将介绍如何用JavaScript实现这一功能。检查输入值是否是数字或字母...
- 图形编辑器开发:快捷键的管理_图形编辑工具
-
大家好,我是前端西瓜哥。...
- 浏览器原生剪贴板:原来它能这样读取用户截图!
-
当我们使用GitHub时,会发现Ctrl+V就能直接读取用户剪贴板图片进行粘贴,那么它是如何工作的?安全性如何?...