以下是我投稿到公司的文章,有做一些更改
前言:
短信业务最底层是真实对对接运营商,使用的cmpp协议发送短信,而在测试环境中,不可能对真实环境来验证短信功能,原因:
1.测试需要发送大量的短信,费用会很高
2.需要模拟各种异常的消息 ,真实运营商有很多规则才能触发异常
3.需要对运营商快速返回各种错误,真实运营商可能会延迟,不利于测试
4.还需要针对大量数据时进行限速设置等各种场景
所以针对以上4点的考虑,最终我们选择自己“创建”一个运营商来处理数据客户端发来的数据,满足日常的短信业务测试
在模拟之前,我们需要对运营商使用的底层协议,交互方式,数据处理方式进行确认,才能更好的建立一个自己的“运营商”。
术语解释
英文缩写 | 英文全称 | 说明 |
---|---|---|
CMPP | China Mobile Peer to Peer | 中国移动点对点协议 |
SP | Service Provider | 业务提供者,即信息资源站实体 |
ISMG | Internet Short Message Gateway | 互联网短信网关 |
SMC | Short Message Center | 短消息中心 |
gevent | python的一个并发框架,以微线程greenlet为核心 | |
struct | Python的模块对字节进行打包和解包 |
1.认识cmpp协议
1.1网络结构
如图1所示,互联网短信网关(ISMG)是外部信息资源站实体(SP)与移动网内短信中心之间的中介实体,互联网短信网关一方面负责接收SP发送给移动用户的信息和提交给短信中心。另一方面,移动用户点播SP业务的信息将由短信中心通过互联网短信网关发给SP。
1.2. CMPP功能概述
以下内容来自cmpp2.0文档的的介绍
短信接收(Short Message Mobile Terminated,SM MT)
典型的业务操作举例如图3所示:
图3 需要前转的MT示意图
- SP发出数据请求(可能是短信通知或手机铃声等),被源ISMG接收;
- 源ISMG对接收到的信息返回响应;
- 源ISMG在本地数据库中找不到手机号段所对应网关代码,向GNS(汇接网关)发路由请求信息;
- 汇接网关将路由信息返回;
- 源ISMG根据路由信息将请求转给目的ISMG;
- 目的ISMG对接收到的信息返回响应;
- 目的ISMG将请求信息发送至SMC;
- SMC向目的ISMG返回响应;
1.2协议栈
CMPP协议以TCP/IP作为底层通信承载,具体结构由图2所示:
图1.2.1CMPP协议栈
ICP与ISMG以Clientmdash;Server方式建立TCP连接,用于双方信息的相互提交。
TCP/IP连接建立后,由Client发起建立应用层的连接,这时如果ICP或ISMG认为需要建立TLS连接,则在传输的数据包中置TLS字段,从而在双方建立TLS连接。
在应用层连接建立后的数据传输过程中,如果ICP或ISMG需要向对端发送加密信息,也可建立TLS连接,这时只需要置相应的消息体中Tls_available(是否使用TLS层)属性字段,本条消息的消息体中的其他属性不发送。
消息采用并发方式发送,加以滑动窗口流量控制,即接收方在应答前一次收到的消息多于窗口大小条将予以拒绝。流程图如图3所示:
图1.2.2 长连接方式
1.2 .消息定义
消息定义是客户端与服务端之间“沟通的方式” 以下是cmpp协议内容的定义 (其中的一个鉴权协议)
基本数据类型
字节类型 | 字节解释说明 |
---|---|
Unsigned Integer | 无符号整数 |
Integer | 整数,可为正整数、负整数或零 |
Octet String | 定长字符串,位数不足时,如果左补0则补ASCII表示的零,如果右补0则补二进制的零 |
消息结构
项目 | 说明 |
---|---|
Message Header | 消息头(所有消息公共包头) |
Octet String | 定长字符串,位数不足时,如果左补0则补ASCII表示的零,如果右补0则补二进制的零 |
消息头格式(Message Header)
字段名 | 字节数 | 类型 | 描述 |
---|---|---|---|
Total_Length | 4 | Unsigned Integer | 消息总长度(含消息头及消息体) |
Command_Id | 4 | Unsigned Integer | 命令或响应类型 |
Sequence_Id | 4 | Unsigned Integer | 消息流水号,顺序累加,步长为1,循环使用(一对请求和应答消息的流水号必须相同) |
CMPP_CONNECT消息定义(SPàISMG)
字段名 | 字节数 | 类型 | 描述 |
---|---|---|---|
Source_Addr | 6 | Octet String | 源地址,此处为SP_Id,即SP的企业代码。 |
Command_Id | 4 | Unsigned Integer | 命令或响应类型 |
Sequence_Id | 4 | Unsigned Integer | 消息流水号,顺序累加,步长为1,循环使用(一对请求和应答消息的流水号必须相同) |
CMPP_CONNECT消息定义
字段名 | 字节数 | 类型 | 描述 |
---|---|---|---|
Source_Addr | 6 | Octet String | 源地址,此处为SP_Id,即SP的企业代码。 |
16 | Unsigned Integer | 用于鉴别源地址。其值通过单向MD5 hash计算得出,表示如下:AuthenticatorSource =MD5(Source_Addr+9 字节的0 +shared secret+timestamp)Shared secret 由中国移动与源地址实体事先商定,timestamp格为MMDDHHMMSS,即月日时分秒,10位。 | |
Version | 1 | Unsigned Integer | 双方协商的版本号(高位4bit表示主版本号,低位4bit表示次版本号) |
Timestamp | 4 | Unsigned Integer | 时间戳的明文,由客户端产生,格式为MMDDHHMMSS,即月日时分秒,10位数字的整型,右对齐 |
CMPP_CONNECT_RESP消息定义(ISMG à SP)
字段名 | 字节数 | 类型 | 描述 |
---|---|---|---|
Status | 1 | Unsigned Integer | 状态0:正确1:消息结构错2:非法源地址3:认证错4:版本太高5~ :其他错误 |
AuthenticatorISMG | 16 | Octet String | ISMG认证码,用于鉴别ISMG。其值通过单向MD5 hash计算得出,表示如下:AuthenticatorISMG =MD5(Status+AuthenticatorSource+shared secret),Shared secret 由中国移动与源地址实体事先商定AuthenticatorSource为源地址实体发送给ISMG的对应消息CMPP_Connect中的值。认证出错时,此项为空 |
Version | 1 | Unsigned Integer | 服务器支持的最高版本号 |
3.构建服务端
3.1短信测试桩架构分析
如图3.1
图3.1
业务处理流程。
如图3.2所示
图3.2
处理流程说明
- 服务端收到一条为39个字节的长度的请求数据,由于头部固定为12字节,剩余为27个字节
- 根据以上的协议27个字节长度判断是需要建立连接
- 获取客户端的连接的用户名与密码,解析AuthenticatorISMG字段验证是否鉴权成功
- 成功进行下一步操作 (需要回应 鉴权成功),在根据建立的连接后,对获取的数据进行处理,返回内容
我们以鉴权部分提取它的主要代码:
取出头部数据包
获取主体内容,鉴权内容
日志展示
3.2效果展示
短信测试桩目前已在测试环境中运行了2年多,帮助公司节省了一大笔测试费用,以下是它展示的效果内容
在iportal上展示,发送成功状态。
在iportal上展示,发送失败状态。
在iportal上展示,已发送的状态。
Iportal上展示,余额不足的状态
3.3短信模拟桩遇到的问题
-
发送的请求数据量已经超过窗口大小
当客户端发送的数量超过了大小处理大小,服务端进行限流,让任务进行排队处理并对它返回响应的code,以减轻服务端压力
-
代码建立无效的链接,浪费资源
建立一个连接,就存储一个连接,当无效链接时,需要删除连接,如何判定无效,心跳连接不在建立,或建立连接后,20分钟内都没有发送消息(测试环境),服务端对断链进行处理
-
语言的选项,以及方案设计
可选择java以及Python语言都可以,考虑Python在公司的使用程度较高,选择Python
选择Python的类有struct模块,用户解析字节数据,使用gevent来解决协程,或者是Python提供的多进程 -
消息粘包
读取数据时,根据头部的长度信息,按序读取ioArgs缓冲区中的数据,若没有达到长度要求,继续读下一个ioArgs解决粘包、半包问题。
我们逐步完善测试桩,它不仅具有解析数据的功能,发送报告,保持心跳、处理不同错误code,我们也提升了处理速度,以前是1分钟处理10个,现在1分钟能处理300个,满足我们大批量数据速度的一个提升,完善了我们各种场景的测试。