JMPP 让 XMPP 协议老树开新花
Posted on Thu 02 January 2025 in Journal
Abstract | JMPP 让 XMPP 协议老树开新花 |
---|---|
Authors | Walter Fan |
Category | learning note |
Status | v1.0 |
Updated | 2025-01-02 |
License | CC-BY-NC-ND 4.0 |
XMPP 是若干年前流行的即时通信 IM 协议, 据说起初的 QQ 就采用了这个协议, 我多年以前也用过它实现过多人聊天, 类似今天的微信群, 时至今日, XMPP 由于采用了 XML 这个冗长的格式, 日趋式微, 我以前就想过用 JSON 来替换 XMPP 中的 XML 格式, 姑且叫它 JMPP(Json Messaging and Presence Protocol) 吧。
XMPP 协议基础知识
XMPP 的核心概念
- 消息(Message):用于传递即时消息。
- 状态(Presence):用户在线状态(例如在线、离线、忙碌)。
- 信息查询(IQ):实现请求-响应模式,用于功能扩展。
- Jabber ID(JID):唯一标识用户的地址,类似于 email 地址,例如
user@domain/resource
。
XMPP 的通信模型
XMPP 使用客户端-服务器架构,通信过程包括: 1. 客户端通过 TCP 连接到服务器。 2. 使用 TLS 加密连接。 3. 通过 SASL 完成身份验证。 4. 交换 XML 消息。
多用户聊天(MUC)规范简介
XMPP 的 MUC(Multi-User Chat)是支持多人聊天室的扩展协议,主要功能包括: 1. 创建和加入聊天室:用户可以创建聊天室或加入现有聊天室。 2. 发送和接收消息:聊天室中的消息会广播给所有成员。 3. 用户状态通知:通知成员加入、离开或更改状态。 4. 权限管理:支持设置管理员、主持人等角色。
MUC 的 XML 消息示例:
<message to='room1@muc.example.com' from='user1@example.com'>
<body>Hello, everyone!</body>
</message>
XMPP 和 XML 的优缺点
XMPP 的优点
- 开放标准:XMPP 基于开放标准 RFC 文档,便于开发者实现和扩展。
- 实时通信:支持低延迟的消息传递,特别适合即时通信场景。
- 可扩展性:XMPP 协议通过命名空间和扩展 XEP(XMPP Extension Protocols)支持多种应用场景。
- 成熟的生态系统:众多开源实现和社区支持。
XMPP 的缺点
- XML 的冗长性:XML 格式结构复杂,数据冗长,解析速度相对较慢。
- 带宽占用高:相比 JSON,XML 消耗更多带宽。
- 现代性不足:随着 RESTful 和 gRPC 等协议的流行,XMPP 显得笨重。
XML 的优点
- 结构化强:通过标签和属性提供严格的层次结构。
- 可验证性:支持通过 DTD 和 Schema 进行格式验证。
- 可读性:人类可读(尽管较繁琐)。
XML 的缺点
- 格式冗长:标签重复导致传输数据量大。
- 解析复杂:相比 JSON,需要更复杂的解析器。
- 现代开发不友好:与现代开发环境中的流行工具链整合较差。
用 JSON 替换 XML 的理由
JSON 是一种轻量级的数据交换格式,与 XML 相比,它具有以下优势:
- 简洁:数据表示更加紧凑,减少冗余。
- 解析快速:现代浏览器和语言库对 JSON 的解析速度快。
- 现代化:与现代开发工具链高度契合。
- 带宽效率:数据量小,更适合移动设备和低带宽环境。
基于以上原因,用 JSON 替换 XMPP 中的 XML 格式是一种合理的改进,我们将其命名为 JMPP(Json Messaging and Presence Protocol)。
JMPP 协议简介
JMPP 继承了 XMPP 的核心思想:即时消息和状态存在通知,同时以 JSON 替代 XML 作为数据表示形式。以下是 JMPP 的基本特性:
- 基于 WebSocket:利用 WebSocket 提供的双向通信能力。
- 轻量化消息结构:所有消息均以 JSON 格式传输,减少数据冗余。
- 兼容性:保留 XMPP 的基本结构,如消息(message)、状态(presence)、查询(iq),但简化表示。
JMPP 消息结构示例:
{
"type": "message",
"from": "user1@example.com",
"to": "user2@example.com",
"body": "Hello, how are you?",
"timestamp": "2025-01-02T10:00:00Z"
}
使用 JMPP 实现多用户聊天(MUC)
为了实现类似 XMPP MUC(Multi-User Chat,多用户聊天)的功能,JMPP 可以定义以下消息类型:
- join: 用户加入聊天室。
- leave: 用户离开聊天室。
- message: 用户发送聊天消息。
- presence: 用户状态更新。
以下是消息结构示例:
{
"type": "join",
"room": "room1",
"user": "user1@example.com"
}
{
"type": "message",
"room": "room1",
"from": "user1@example.com",
"body": "Hello, everyone!",
"timestamp": "2025-01-02T10:05:00Z"
}
使用 FastAPI 和 WebSocket 实现 JMPP 聊天室
服务器端实现
from fastapi import FastAPI, WebSocket, WebSocketDisconnect
from typing import List, Dict
import json
app = FastAPI()
class RoomManager:
def __init__(self):
self.rooms: Dict[str, List[WebSocket]] = {}
async def connect(self, websocket: WebSocket, room: str):
await websocket.accept()
if room not in self.rooms:
self.rooms[room] = []
self.rooms[room].append(websocket)
def disconnect(self, websocket: WebSocket, room: str):
if room in self.rooms:
self.rooms[room].remove(websocket)
if not self.rooms[room]:
del self.rooms[room]
async def broadcast(self, room: str, message: dict):
if room in self.rooms:
for client in self.rooms[room]:
await client.send_text(json.dumps(message))
room_manager = RoomManager()
@app.websocket("/ws/{room}/{user}")
async def websocket_endpoint(websocket: WebSocket, room: str, user: str):
await room_manager.connect(websocket, room)
try:
join_message = {"type": "presence", "room": room, "user": user, "status": "joined"}
await room_manager.broadcast(room, join_message)
while True:
data = await websocket.receive_text()
message = json.loads(data)
await room_manager.broadcast(room, message)
except WebSocketDisconnect:
leave_message = {"type": "presence", "room": room, "user": user, "status": "left"}
room_manager.disconnect(websocket, room)
await room_manager.broadcast(room, leave_message)
客户端实现
import asyncio
import websockets
import json
async def jmpp_client():
uri = "ws://localhost:8000/ws/room1/user1"
async with websockets.connect(uri) as websocket:
# 加入聊天室
join_message = {"type": "join", "room": "room1", "user": "user1@example.com"}
await websocket.send(json.dumps(join_message))
async def receive():
while True:
response = await websocket.recv()
print(f"Received: {response}")
async def send():
while True:
text = input("Enter message: ")
message = {
"type": "message",
"room": "room1",
"from": "user1@example.com",
"body": text,
"timestamp": "2025-01-02T10:05:00Z"
}
await websocket.send(json.dumps(message))
await asyncio.gather(receive(), send())
asyncio.run(jmpp_client())
总结
JMPP 通过用 JSON 替换 XMPP 的 XML,不仅简化了消息格式,还提升了效率。结合 WebSocket 和 FastAPI 的多用户聊天功能,JMPP 提供了一种现代化、轻量级的即时通信协议实现方案,适合构建聊天室、状态通知等场景。未来,可以进一步扩展协议,支持更多功能,如加密、用户验证和消息历史存储。
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。