百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术分析 > 正文

Tornado整合socketio(一)

liebian365 2024-11-26 05:52 16 浏览 0 评论

最近在做的项目中,需要将手机中的视频流或者音频流发送给服务端,再由服务端转发给浏览器端,起初我使用redis作为中转,将数据发到redis中,再由redis的发布订阅功能,整体架构如下

主要是利用了redis的pub/sub功能,这种方案也没有什么问题,但是整体的性能瓶颈受redis的影响。

最近接触到socketio,发现这种需求可以使用它来实现,但是网上查找了一些资料,在python的使用中,主要还是flask-socketio与原生的应用上,由于目前项目使用Tornado来构建,所以用了几天时间将socketio与Tornado的融合使用。

本教程会分几篇来介绍,主要以下几个章节

  1. socketio介绍与脚手架的搭建
  2. 定义消息处理事件
  3. 命名空间的使用
  4. 消息的发布与响应
  5. room的使用
  6. 前端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方法。

connectdisconnect 事件是特殊的事件,在客户端进行连接和断开连接时自动调用

@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_eventmy 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字符串复制...

二年级上册语文必考句子仿写,家长打印,孩子照着练

二年级上册语文必考句子仿写,家长打印,孩子照着练。具体如下:...

一年级语文上 句子专项练习(可打印)

...

亲自上阵!C++ 大佬深度“剧透”:C++26 将如何在代码生成上对抗 Rust?

...

取消回复欢迎 发表评论: