用Python实现Cmpp协议的教程

引言&协议概述

(CMPP)是中国移动为实现短信业务而制定的一种通信协议,用于在客户端(SP,Service Provider)和中国移动短信网关之间传输短消息,有时也叫做移动梦网短信业务。CMPP3.0是该协议的第三个版本,相比于前两个版本,它增加了对长短信的支持、优化了数据结构等。本文对CMPP协议进行介绍,并给出Python实现CMPP协议栈的思路。

Python的asyncio模块提供了一套简洁的异步IO编程模型,非常适合用于实现协议栈。

CMPP协议基于客户端/服务端模型工作。由客户端(短信应用,如手机,应用程序等)先和ISMG(Internet Short Message Gateway 互联网短信网关)建立起TCP长连接,并使用CMPP命令与ISMG进行交互,实现短信的发送和接收。在CMPP协议中,无需同步等待响应就可以发送下一个指令,实现者可以根据自己的需要,实现同步、异步两种消息传输模式,满足不同场景下的性能要求。

连接成功,发送短信并查询短信发送成功

连接成功,从ISMG接收到短信

协议帧介绍

在CMPP协议中,每个PDU都包含两个部分:CMPP Header和CMPP Body。

image.png

CMPP Header

Header包含以下字段,大小长度都是4字节

  • Total Length:整个PDU的长度,包括Header和Body。
  • Command ID:用于标识PDU的类型(例如,Connect、Submit等)。
  • Sequence Id:序列号,用来匹配请求和响应。

用Python Asyncio实现CMPP协议栈里的建立连接

可以以本文的代码作为基础,很容易地在上面扩展。

代码结构组织如下:

.
├── LICENSE
├── README.md
├── cmpp
│   ├── __init__.py
│   ├── client.py
│   ├── protocol.py
│   └── utils.py
├── requirements.txt
├── setup.cfg
└── setup.py
  • cmpp/protocol.py:定义不同 CMPP 协议数据单元 (PDU) 的数据类,包括 CmppHeader、CmppConnect、CmppConnectResp、CmppSubmit 和 CmppSubmitResp
  • cmpp/client.py:该类处理与 ISMG(互联网短消息网关)的连接以及发送/接收 PDU。 主要 asyncio 进行异步 I/O 操作
  • cmpp/utils.py:定义 BoundAtomic 类,它是一种线程安全的方式来管理具有最小值和最大值的序列号。保证CMPP序列号在一定的范围内
  • setup.py:配置要分发的包,指定包名称、版本、作者和依赖项等元数据。

利用Python锁实现sequence_id

sequence_id是从1到0x7FFFFFFF的值

import threadingclass BoundAtomic:def __init__(self, min_val: int, max_val: int):assert min_val <= max_val, "min must be less than or equal to max"self.min = min_valself.max = max_valself.value = min_valself.lock = threading.Lock()def next_val(self) -> int:with self.lock:if self.value >= self.max:self.value = self.minelse:self.value += 1return self.value

在Python中定义CMPP PDU,篇幅有限,仅定义数个PDU

from dataclasses import dataclass
from typing import Union, List@dataclass
class CmppHeader:total_length: intcommand_id: intsequence_id: int@dataclass
class CmppConnect:source_addr: strauthenticator_source: bytesversion: inttimestamp: int@dataclass
class CmppConnectResp:status: intauthenticator_ismg: strversion: int@dataclass
class CmppSubmit:msg_id: intpk_total: intpk_number: intregistered_delivery: intmsg_level: intservice_id: strfee_user_type: intfee_terminal_id: strfee_terminal_type: inttp_pid: inttp_udhi: intmsg_fmt: intmsg_src: strfee_type: strfee_code: strvalid_time: strat_time: strsrc_id: strdest_usr_tl: intdest_terminal_id: List[str]dest_terminal_type: intmsg_length: intmsg_content: byteslink_id: str@dataclass
class CmppSubmitResp:msg_id: intresult: int@dataclass
class CmppPdu:header: CmppHeaderbody: Union[CmppHeader, CmppConnectResp, CmppSubmit, CmppSubmitResp]

实现编解码方法

@dataclass
class CmppConnect:source_addr: strauthenticator_source: bytesversion: int# MMDDHHMMSS formattimestamp: intdef encode(self) -> bytes:source_addr_bytes = self.source_addr.encode('utf-8').ljust(6, b'\x00')version_byte = self.version.to_bytes(1, 'big')timestamp_bytes = self.timestamp.to_bytes(4, 'big')return source_addr_bytes + self.authenticator_source + version_byte + timestamp_bytes@dataclass
class CmppConnectResp:status: intauthenticator_ismg: strversion: int@staticmethoddef decode(data: bytes) -> 'CmppConnectResp':status = int.from_bytes(data[0:4], 'big')authenticator_ismg = data[4:20].rstrip(b'\x00').decode('utf-8')version = data[20]return CmppConnectResp(status=status, authenticator_ismg=authenticator_ismg, version=version)@dataclass
class CmppPdu:header: CmppHeaderbody: Union[CmppConnect, CmppConnectResp, CmppSubmit, CmppSubmitResp]def encode(self) -> bytes:body_bytes = self.body.encode()self.header.total_length = len(body_bytes) + 12header_bytes = (self.header.total_length.to_bytes(4, 'big') +self.header.command_id.to_bytes(4, 'big') +self.header.sequence_id.to_bytes(4, 'big'))return header_bytes + body_bytes@staticmethoddef decode(data: bytes) -> 'CmppPdu':header = CmppHeader(total_length=int.from_bytes(data[0:4], 'big'),command_id=int.from_bytes(data[4:8], 'big'),sequence_id=int.from_bytes(data[8:12], 'big'))body_data = data[12:header.total_length]if header.command_id == CONNECT_RESP_ID:body = CmppConnectResp.decode(body_data)else:raise NotImplementedError("not implemented yet.")return CmppPdu(header=header, body=body)

asyncio tcp流相关代码

class CmppClient:def __init__(self, host: str, port: int):self.host = hostself.port = portself.sequence_id = BoundAtomic(1, 0x7FFFFFFF)self.reader = Noneself.writer = Noneasync def connect(self):self.reader, self.writer = await asyncio.open_connection(self.host, self.port)async def close(self):if self.writer:self.writer.close()

实现同步的connect_ismg方法

    async def connect_ismg(self, request: CmppConnect):if self.writer is None or self.reader is None:raise ConnectionError("Client is not connected")sequence_id = self.sequence_id.next_val()header = CmppHeader(0, command_id=CONNECT_ID, sequence_id=sequence_id)pdu: CmppPdu = CmppPdu(header=header, body=request)self.writer.write(pdu.encode())await self.writer.drain()length_bytes = await self.reader.readexactly(4)response_length = int.from_bytes(length_bytes)response_data = await self.reader.readexactly(response_length)return CmppPdu.decode(response_data)

运行example,验证连接成功

async def main():client = CmppClient(host='localhost', port=7890)await client.connect()print("Connected to ISMG")connect_request = CmppConnect(source_addr='source_addr',authenticator_source=b'authenticator_source',version=0,timestamp=1122334455,)connect_response = await client.connect_ismg(connect_request)print(f"Connect response: {connect_response}")await client.close()print("Connection closed")asyncio.run(main())

image.png

总结

本文简单对CMPP协议进行了介绍,并尝试用python实现协议栈,但实际商用发送短信往往更加复杂,面临诸如流控、运营商对接、传输层安全等问题,可以选择华为云消息&短信(Message & SMS)服务

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/50604.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式Linux学习: 设备树实验

设备树&#xff08;DeviceTree&#xff09;是一种硬件描述机制&#xff0c;用于在嵌入式系统和操作系统中描述硬件设备的特性、连接关系和配置信息。它提供了一种与平台无关的方式来描述硬件&#xff0c;使得内核与硬件之间的耦合度降低&#xff0c;提高了系统的可移植性和可维…

立创梁山派--移植开源的SFUD和FATFS实现SPI-FLASH文件系统

本文主要是在sfud的基础上进行fatfs文件系统的移植&#xff0c;并不对sfud的移植再进行过多的讲解了哦&#xff0c;所以如果想了解sfud的移植过程&#xff0c;请参考我的另外一篇文章&#xff1a;传送门 正文开始咯 首先我们需要先准备资料准备好&#xff0c;这里对于fatfs的…

【ESP32 IDF 软件模拟SPI驱动 W25Q64存储与读取数组】

目录 SPISPI介绍SPI时序代码编写&#xff08;spi&w25q64&#xff09; 代码调试 SPI SPI介绍 SPI&#xff08;Serial Peripheral Interface&#xff0c;串行外围设备接口&#xff09;是一种高速、全双工、同步的串行通信总线&#xff0c;常用于微控制器与各种外围设备&…

苍穹外卖浏览器前端界面修改

背景&#xff1a; 客户原始方案是期望做一个Spring Boot Vue的饿了么系统&#xff0c;但时间上太仓促&#xff0c;所以建议选择开源的苍穹外码目作为作业提交。 客户接受了建议的方案后&#xff0c;期望对前端页面做一些个性化的定制修改。 过程&#xff1a; 苍穹外卖简单介…

【HTML+CSS】HTML超链接:构建网页导航的基石

目录 什么是HTML超链接&#xff1f; 基本语法 示例 链接到另一个网页 链接到同一页面内的不同部分 常用属性 在Web开发的广阔世界中&#xff0c;HTML&#xff08;HyperText Markup Language&#xff09;作为网页内容的标准标记语言&#xff0c;扮演着至关重要的角色。而在…

重拾CSS,前端样式精读-函数(颜色,计算,图像和图形)

前言 本文收录于CSS系列文章中&#xff0c;欢迎阅读指正 在计算机编程中&#xff0c;函数有着重要的作用和意义&#xff0c;它可以实现封装&#xff0c;复用&#xff0c;模块化&#xff0c;参数等功能效果&#xff0c;在如何在CSS中写变量&#xff1f;一文带你了解前端样式利…

操作系统杂项(十)

目录 一、简述socket中select、epoll的使用场景和区别 1、使用场景 2、区别 二、epoll水平触发和边缘触发的区别 三、简述Reactor和Proactor模式 1、Reactor 2、Proactor 3、区别 四、简述同步和异步的区别&#xff0c;阻塞和非阻塞的区别 1、同步与异步 2、阻塞与非…

深入分析 Android ContentProvider (五)

文章目录 深入分析 Android ContentProvider (五)ContentProvider 的性能优化和实践案例1. 性能优化技巧1.1. 数据库索引优化示例&#xff1a;添加索引 1.2. 批量操作与事务管理示例&#xff1a;批量插入操作 1.3. 使用异步操作示例&#xff1a;使用 AsyncTask 进行异步查询 1.…

Linux:基础

一、安装 二、 一些组件 2.1 git管理 集中式版本控制系统:版本库是集中存放在中央服务器的,需要时要先从中央服务器取得最新的版本进行修改,修改后再推送给中央服务器。集中式版本控制系统最大的毛病就是必须联网才能工作,网速慢的话影响太大。 分布式版本控制系统:分布…

Linux网络-wget命令

作者介绍&#xff1a;简历上没有一个精通的运维工程师。希望大家多多关注我&#xff0c;我尽量把自己会的都分享给大家&#xff0c;下面的思维导图也是预计更新的内容和当前进度(不定时更新)。 Linux服务器作为一个常用的网络服务器&#xff0c;主要的作用就是向客户端提供网络…

设计模式14-享元模式

设计模式14-享元模式 由来动机定义与结构代码推导特点享元模式的应用总结优点缺点使用享元模式的注意事项 由来动机 在很多应用中&#xff0c;可能会创建大量相似对象&#xff0c;例如在文字处理器中每个字符对象。在这些场景下&#xff0c;如果每个对象都独立存在&#xff0c…

PyCharm 2024.1.4:一站式教程与新特性解析

简介 PyCharm是由JetBrains开发的一款Python集成开发环境&#xff08;IDE&#xff09;&#xff0c;自发布以来&#xff0c;凭借其强大的功能、智能的代码补全、广泛的插件支持和用户友好的界面&#xff0c;成为了Python开发者的首选工具之一。无论是数据科学、Web开发还是其他…

Redis - SpringDataRedis - RedisTemplate

目录 概述 创建项目 引入依赖 配置文件 测试代码 测试结果 数据序列化器 自定义RedisTemplate的序列化方式 测试报错 添加依赖后测试 存入一个 String 类型的数据 测试存入一个对象 优化 -- 手动序列化 测试存入一个Hash 总结&#xff1a; 概述 SpringData 是 S…

在 ArchLinux 上编译运行 axmol 引擎

本文将在 Windows 10 上安装 Arch WSL 中编译 axmol 请确保 WSL2 已正确安装 1. 在微软应用商店安装 ArchLinux 2. 打开 Arch&#xff0c;按照提示输入用户名和密码&#xff0c;尽量简单 3. 配置清华源&#xff0c;速度快的起飞&#xff0c;否则&#xff0c;各种包会安装失败…

光伏电站气象站:现代光伏系统的重要组成部分

光伏电站气象站&#xff0c;作为现代光伏系统的重要组成部分&#xff0c;集成了气象学、电子信息技术、数据处理与分析等多学科技术于一体&#xff0c;能够实时监测并记录包括温度、湿度、风速、风向、太阳辐射强度、降雨量在内的多种气象参数。这些数据不仅是评估光伏板发电效…

GLSL教程 第8章:几何着色器

目录 8.1 几何着色器的介绍 几何着色器的主要功能&#xff1a; 几何着色器的工作流程&#xff1a; 8.2 实现基本的几何变换 示例&#xff1a;将三角形扩展成多个三角形 8.3 几何着色器的高级应用 1. 粒子系统 2. 光晕效果 3. 线框模型 小结 几何着色器是图形管线中的一…

应用层自定义协议以及序列化和反序列化

文章目录 应用层自定义协议以及序列化和反序列化1、应用层自定义协议1.1、应用层1.2、协议 2、序列化和反序列化3、TCP 为什么支持全双工4、jsoncpp基础4.1、序列化4.2、反序列化 5、实现网络版计算器6、手写序列化和反序列化 应用层自定义协议以及序列化和反序列化 1、应用层…

爬取贴吧的标题和链接

免责声明 感谢您学习本爬虫学习Demo。在使用本Demo之前&#xff0c;请仔细阅读以下免责声明&#xff1a; 学习和研究目的&#xff1a;本爬虫Demo仅供学习和研究使用。用户不得将其用于任何商业用途或其他未经授权的行为。合法性&#xff1a;用户在使用本Demo时&#xff0c;应确…

智能算法驱动的爬虫平台:解锁网络数据的无限潜力

摘要 在信息爆炸的时代&#xff0c;网络数据如同深海宝藏&#xff0c;等待着有识之士发掘其无尽价值。本文将探索智能算法驱动的爬虫平台如何成为解锁这一宝库的关键&#xff0c;不仅剖析其技术优势&#xff0c;还通过实例展示它如何助力企业与开发者高效、稳定地采集数据&…

C语言 ——— 数组指针的定义 数组指针的使用

目录 前言 数组指针的定义 数组指针的使用 前言 之前有编写过关于 指针数组 的相关知识 C语言 ——— 指针数组 & 指针数组模拟二维整型数组-CSDN博客 指针数组 顾名思义就是 存放指针的数组 那什么是数组指针呢&#xff1f; 数组指针的定义 何为数组指针&#xf…