最近在研究Python下整合FastAPI的Socket.IO 应用,对于其WebSocket的消息报文协议进行了深入了解,并整理了相关的协议内容,整合到FastAPI的WebSocket通讯处理中,用作多端的消息通讯,如聊天,系统信息通知等。

1. 总体设计

  • 统一事件名:客户端和服务端只监听/发送一个事件,例如:"message"。
  • 消息类型区分:通过 msgtype 字段区分不同的业务逻辑。
  • 通用字段:每条消息都必须包含的基础字段(如 msgtype, sender, timestamp)。
  • 扩展字段:放在 payload 里,根据不同类型自由定义。
  • 报文格式:统一为 JSON 格式。
  • 广播机制:服务端可以广播消息到所有客户端,方便消息的实时推送。
  • 房间机制:服务端可以创建、加入或离开房间,方便消息的分发。

2. 消息格式

{
"msgtype": "string", // 消息类型,如 "chat", "notification", "command"
"sender": "string", // 发送者标识,如用户ID或系统标识
"timestamp": "number", // 消息发送时间戳(毫秒)
"payload": { // 消息内容(不同 msgtype 有不同格式)
// 根据 msgtype 定义不同的结构
}, "room": "string", // 房间标识(可选),表示消息所属的房间
"touser": "string", // 接收者标识(可选),表示消息的目标用户,多个接收者用‘|’分隔
"totag": "string", // 接收者标签(可选),表示消息的目标用户标签,多个标签用‘|’分隔
}

例子:

{
"msgtype": "chat",
"room": "room1",
"sender": "user1",
"timestamp": 1612345678901,
"payload": {
"text": "Hello, world!"
}
}

3. 消息类型

3.1 系统类消息(system)

系统类消息由系统发送,用于通知客户端或服务端状态变化。

{
"msgtype": "system_notice",
"sender": "system",
"timestamp": 1694412000000,
"payload": {
"level": "info", // info, warning, error
"text": "服务器即将维护"
}
}

3.2 聊天类消息(chat)

聊天类消息由用户发送,用于聊天。

{
"msgtype": "chat_message",
"room": null,
"sender": "alice",
"timestamp": 1694412000000,
"payload": {
"text": "你好,大家!",
"extra": {
"emoji": ""
}
}
}

3.3 房间操作类消息(room)

房间操作类消息用于创建、加入或离开房间。

{
"msgtype": "room_event",
"room": "room1",
"sender": "bob",
"timestamp": 1694412000000,
"payload": {
"action": "join", // join, leave, create, destroy
"user": "bob"
}
}

3.4 ACK / 状态反馈消息(ack)

ACK / 状态反馈消息用于确认客户端或服务端收到消息。

{
"msgtype": "ack",
"sender": "server",
"timestamp": 1694412000000,
"payload": {
"status": "ok", // ok, fail
"requestId": "abc123",
"msg": "消息已送达"
}
}

3.5 命令类消息(command)

命令类消息用于向服务端发送指令。

{
"msgtype": "command",
"sender": "user1",
"timestamp": 1694412000000,
"payload": {
"command": "create_room",
"args": {
"name": "room1"
}
}
}

3.6 图片类消息(image)

图片类消息用于发送图片。

{
"msgtype": "image",
"room": "room1",
"sender": "user1",
"timestamp": 1694412000000,
"payload": {
"url": "https://example.com/image.jpg"
}
}

3.7 文件类消息(file)

文件类消息用于发送文件。

{
"msgtype": "file",
"room": "room1",
"sender": "user1",
"timestamp": 1694412000000,
"payload": {
"url": "https://example.com/file.pdf"
}
}

3.8 语音类消息(voice)

3.9 视频类消息(video)

3.10 位置类消息(location)

3.11 其他类型消息(other)

3.12 自定义消息(custom)

4、Python 服务端示例

import time
import socketio sio = socketio.AsyncServer(async_mode="asgi", cors_allowed_origins="*") def make_message(msg_type, sender, payload, room=None):
return {
"msgtype": msg_type,
"room": room,
"sender": sender,
"timestamp": int(time.time() * 1000),
"payload": payload
} @sio.event
async def chat_message(sid, data):
msg = make_message("chat_message", sid, {"text": data})
await sio.emit("message", msg) # 广播 @sio.event
async def join_room(sid, room):
sio.enter_room(sid, room)
msg = make_message("room_event", "system", {"action": "join", "user": sid}, room)
await sio.emit("message", msg, room=room)

5、Vue3 前端示例

socket.on("message", (msg) => {
switch (msg.msgtype) {
case "chat_message":
console.log(`[${msg.sender}] 说: ${msg.payload.text}`);
break; case "system_notice":
console.log(`系统通知: ${msg.payload.text}`);
break; case "room_event":
console.log(`房间 ${msg.room} 事件: ${msg.payload.action} -> ${msg.payload.user}`);
break; case "ack":
console.log(`ACK: ${msg.payload.status} (${msg.payload.msg})`);
break; default:
console.warn("未知消息类型:", msg);
}
});

6、通信流程示例

6.1 普通消息

  • 客户端发送:
{
"msgtype": "chat_message",
"sender": "alice",
"timestamp": 1694412000000,
"payload": {
"text": "Hello World"
}
}
  • 服务端广播:
{
"msgtype": "chat_message",
"sender": "alice",
"timestamp": 1694412000500,
"payload": {
"text": "Hello World"
}
}

6.2 房间消息

6.2.1 创建房间

  1. 客户端发送:
{
"msgtype": "command",
"sender": "bob",
"timestamp": 1694412000000,
"payload": {
"command": "create_room",
"args": {
"name": "room1"
}
}
} 2. 服务端响应: ```json
{
"msgtype": "ack",
"sender": "server",
"timestamp": 1694412000000,
"payload": {
"status": "ok",
"requestId": "abc123",
"msg": "房间 room1 创建成功"
}
}

6.2.2 加入房间

1.客户端发送:

{
"msgtype": "room_event",
"sender": "alice",
"timestamp": 1694412000000,
"payload": {
"action": "join",
"user": "alice"
}
}

2.服务端响应:

{
"msgtype": "ack",
"sender": "server",
"timestamp": 1694412000000,
"payload": {
"status": "ok",
"requestId": "abc123",
"msg": "用户 alice 加入房间 room1"
}
}

3.服务端广播:

{
"msgtype": "room_event",
"room": "room1",
"sender": "server",
"timestamp": 1694412000000,
"payload": {
"action": "join",
"user": "alice"
}
}

6.2.3 离开房间

1.客户端发送:

{
"msgtype": "room_event",
"sender": "alice",
"timestamp": 1694412000000,
"payload": {
"action": "leave",
"user": "alice"
}
}

2.服务端响应:

{
"msgtype": "ack",
"sender": "server",
"timestamp": 1694412000000,
"payload": {
"status": "ok",
"requestId": "abc123",
"msg": "用户 alice 离开房间 room1"
}
}

7、优点

  • 统一事件名:客户端和服务端只监听/发送一个事件,降低耦合度,提高代码可读性。

  • 消息类型区分:通过 msgtype 字段区分不同的业务逻辑,提高消息处理效率。

  • 通用字段:每条消息都必须包含的基础字段(如 msgtype, sender, timestamp),方便服务端处理。

  • 扩展字段:放在 payload 里,根据不同类型自由定义,方便消息内容的扩展。

  • 状态反馈消息:ACK / 状态反馈消息用于确认客户端或服务端收到消息,方便客户端处理。

  • 命令类消息:命令类消息用于向服务端发送指令,方便服务端处理。

  • 广播机制:服务端可以广播消息到所有客户端,方便消息的实时推送。

  • 房间机制:服务端可以创建、加入或离开房间,方便消息的分发。

  • 扩展性:服务端和客户端可以自由扩展消息类型,增加更多的业务逻辑。

  • 兼容性:服务端和客户端可以兼容 SocketIO 协议,方便与其他服务端和客户端通信。

  • 易用性:服务端和客户端可以直接使用 SocketIO 库,降低学习成本。

  • 性能:服务端和客户端可以利用 WebSocket 协议,提高消息传输效率。

8、关于 Socket.IO的挂载的注意事项

Socket.IO 是一个基于 WebSocket 的实时通信协议,它可以实现浏览器和服务器之间的双向通信。

Socket.IO 协议的实现主要依赖于两个部分:

  • Socket.IO 服务器:Socket.IO 服务器负责维护连接,处理消息,并向客户端推送消息。
  • Socket.IO 客户端:Socket.IO 客户端负责维护连接,向 Socket.IO 服务器发送消息,并接收消息。

如果你用 app = register_app(),然后用 uvicorn main:app 或者 config = uvicorn.Config(app, ...),这样启动的“app”是原生 FastAPI 实例,不包含 Socket.IO 的功能,所以前端 Socket.IO 客户端连接时不会触发 @sio.event 的回调!

解决方法:

你需要让 Uvicorn 启动的是 ASGI 的组合应用,即 sio_app,而不是纯 FastAPI 的 app。


import socketio
from fastapi import FastAPI def register_app():
app = FastAPI()
# ...注册路由等...
return app app = register_app()
sio = socketio.AsyncServer(async_mode='asgi', cors_allowed_origins='*')
sio_app = socketio.ASGIApp(sio, other_asgi_app=app) # main.py 入口
if __name__ == "__main__":
import uvicorn
# 下面两种写法都可以
uvicorn.run(sio_app, host="0.0.0.0", port=8000)
# 或
# config = uvicorn.Config(sio_app, host="0.0.0.0", port=8000)
# server = uvicorn.Server(config)
# server.run()

注意:一定要启动的是 sio_app,不是 app!

你可以继续用 register_app() 组织你的 FastAPI 配置,但最终给 Uvicorn 的必须是 sio_app。

最后我们来看看再FastAPI项目上对接的例子通讯截图(尚未完工,持续改进优化中)。

服务端的信息截图



客户端信息截图

基于 SocketIO 消息协议规范,并构建FastAPI上的SocketIO 应用的更多相关文章

  1. 即插即用,基于阿里云Ganos快速构建云上开源GIS方案

    对于轻量级GIS应用,选择具备时空能力的云上数据库再搭配开源GIS软件,能够快速构建稳定.廉价.实用的GIS解决方案.Ganos是阿里云自研时空基础设施(PaaS层)的核心引擎,该引擎整合了云上异构计 ...

  2. 物联网网关开发:基于MQTT消息总线的设计过程(上)

    道哥的第 021 篇原创 目录 一.前言 二.网关的作用 2.1 指令转发 2.2 外网通信 2.3 协议转换 2.4 设备管理 2.5 边沿计算(自动化控制) 三.网关内部进程之间的通信 3.1 网 ...

  3. .Net Core with 微服务 - 使用 AgileDT 快速实现基于可靠消息的分布式事务

    前面对于分布式事务也讲了好几篇了(可靠消息最终一致性 分布式事务 - TCC 分布式事务 - 2PC.3PC),但是还没有实战过.那么本篇我们就来演示下如何在 .NET 环境下实现一个基于可靠消息的分 ...

  4. 码途有道----基于系统观的核心能力构建-by-韩宏老师

    原文链接:http://blog.sina.com.cn/s/blog_7d5a09f90102v341.html 有感于同学们在大学中如何学习计算机技术有些感概,将我书(老码识途)中的序言整理了一下 ...

  5. AngularJS是为了克服HTML在构建应用上的不足而设计的

    AngularJS中文网:http://www.apjs.net/ 简介   AngularJS是为了克服HTML在构建应用上的不足而设计的.HTML是一门很好的为静态文本展示设计的声明式语言,但要构 ...

  6. 基于Kafka消息驱动最终一致事务(一)

    基本可用软状态最终一致事务 本用例分两个数据库分别是用户库和交易库,不使用分布式事务,使用基于消息驱动实现基本可用软状态最终一致事务(BASE).现在说明下事务逻辑演化步骤,尊从CAP原则,即分布式系 ...

  7. 如何基于 K8S 多租能力构建 Serverless Container

    当前 Kubernetes 已经成为名副其实的企业级容器编排规范,很多云平台都开始提供兼容 Kubernetes 接口的容器服务.而在多用户支持方面,多数平台选择直接提供专属虚机集群,用户需要花费大量 ...

  8. 基于jersey和Apache Tomcat构建Restful Web服务(二)

    基于jersey和Apache Tomcat构建Restful Web服务(二) 上篇博客介绍了REST以及Jersey并使用其搭建了一个简单的“Hello World”,那么本次呢,再来点有趣的东西 ...

  9. 基于rabbitMQ 消息延时队列方案 模拟电商超时未支付订单处理场景

    前言 传统处理超时订单 采取定时任务轮训数据库订单,并且批量处理.其弊端也是显而易见的:对服务器.数据库性会有很大的要求,并且当处理大量订单起来会很力不从心,而且实时性也不是特别好 当然传统的手法还可 ...

  10. 基于Redis消息的订阅发布应用场景

    目录 基于Redis消息的订阅发布应用场景 1.应用背景 2.困境 2.1 锁表风险 2.2 实时性差 2.3 增加编程复杂性 2.4 实时效果 3.解决方案 3.1 前端传值给服务端 3.2 服务端 ...

随机推荐

  1. java----IO字节流读写文件

    IO流 IO流分类 如果是按照数据的流向划分: 输入流 输出流 判断输入还是输出流 以当前程序做参照物,观察数据是流入还是流出,如果流出,则使用输出流,如果数据是流入,则使用输入流. 如果按照处理的单 ...

  2. docusaurus简单使用

    前言 docusaurus是一款使用markdown编写手册文档的工具,同类竞品有vitePress (放弃不维护的vuepress吧) 目前来看,比后者多了10k个start. docusaurus ...

  3. 企业微信公众号本地调试auto2.0

    适配开发者工具 企业微信公众号.微信公众号本质相同,因为我在开发企业号,所以拿企业号为例 首先添加企业应用 然后进入改应用,配置主页.网页授权及JS-SDK.企业微信授权登录 注意企业微信不允许配置l ...

  4. 前端开发系列120-进阶篇之deepClone

    本文讨论数据的拷贝,并给出深拷贝的实现代码. 拷贝即复制( copy | clone ),获取指定数据副本的一种行为,理论上我们可以对任意类型的数据进行拷贝,包括但不限于null.undefined. ...

  5. java 中 sleep & wait 的区别

    简介 简单来说 一个和线程同步有关, 一个单纯的线程延迟等待. 1.sleep是线程中的方法,但是wait是Object中的方法. 2.sleep方法不会释放lock,但是wait会释放,而且会加入到 ...

  6. fowsniff WP

    下载地址: https://download.vulnhub.com/fowsniff/Fowsniff_CTF_ova.7z category:重要 awk剪切得到字典,巩固awk使用技巧 motd ...

  7. RestCloud ETL实践之无标识位实现增量数据同步

    步骤1: 从平台首页进入数据源管理 步骤2: 新建数据源 步骤3:选择需要连接的数据源类型 步骤4:填写数据源相关配置 步骤5: 测试连接数据库 步骤6: 从平台首页进入数据集成开发模块 步骤7: 新 ...

  8. ICEE-RF Trisistors-

    Radio frequency (RF) transistors RF Transistor subcategories Low Noise RF Transistors High Linearity ...

  9. Rust中的匿名函数与闭包

    一.匿名函数 语法:"|参数名| 语句" 参考下面的这个示例: fn add(a: i32, b: i32) -> i32 { a + b } fn main() { let ...

  10. unity代码编译时间分析工具

    https://github.com/needle-tools/compilation-visualizer 工具2 Editor Iteration Profiler (EIP) 地址: https ...