一、CMPP协议简介
中国移动通信互联网短信网关接口协议(China Mobile Peer to Peer CMPP),是中国移动梦网内部各SMS参与节点相互交换SMS的官方协议。作为梦网的参与方,移动梦网的增值服务商(Service Provider SP )要按照此协议规范实现SP的部分,才可以将自己的短信通过移动的GSM网络的数据通道传输到最终手机用户上。
实际上,协议规范了3个方面的内容:
。SP与移动的互联网短信网关(Internet Short Message Gateway,ISMG)之间的接口协议
。ISMG之间的接口协议(譬如移动各省、市之间的短信息交换通过ISMG之间进行)
。ISMG与汇接网关(Gateway Name Server GNS,类似互联网上的DNS服务器)之间的接口协议,譬如跨省之类的短信需要GNS的帮助指出当前ISMG该如何传递短信。
其中,后二方面属于移动短信息系统内部实现,对于SP来讲大概可以“透明”来看待,只要实现了SP同ISMG的正确交互,就可以实现接入移动梦网短信系统。我们关心的只是SP端的开发细节。
二、CMPP交互模式
从手机用户角度讲,按短信的发起/接收路径来讲,有两个叫法:
MT(Short Message Mobile Terminated, SMMT),短信接收,短信从SP发送到手机用户。
MO (Short Message Mobile Originate,SMMO),短信发送,短信从手机用户端发送到目标SP。
这两类短信交互,从SP端来看,都是属于Socket传输应用,CMPP的协议是以TCP/IP协议作为底层承载协议的,属于TCP/IP协议栈之上的应用。
SP同ISMG的交互连接分
长连接和
短连接。
所谓短连接,就是一次连接,传输一个消息,然后等待回复后拆除连接,显然,效率很低,所以,基本上不被考虑.所谓长连接,就是SP建立同ISMG连接,然后不断将数据包(一个个CMPP消息)发送到ISMG,此处发送不必等待某条消息的ISMG回应消息返回,就接着发送下一个消息。同时,等待ISMG返回信息或者等待ISMG发送给SP的消息。发送同接收消息不是一定要同步的,实际采用异步(同时也时双工)模式。从效率上,显然,必须全双工的异步模式才能够满足实际应用需求。
如下图(摘自CMPP2.0官方文档)所示,演示了长连接模式数据传输过程:
![](https://p-blog.csdn.net/images/p_blog_csdn_net/shanhe/71478/r_LongLink.jpg)
三、SP端开发
1.
消息分类
首先,图中的CMPP消息有很多种,SP同ISMG之间交流这些消息。大体上这些消息发出后,对方往往需要回复一个应答(RESP)类消息。注意,这些消息大多具有方向性,也就是说只能够从一端到另一端,而不可反方向进行,有些(少数)则可两端都能够发出。以下信息主要来源于移动的文档,但针对大家易混淆或源文档解释不够详细做了明确和补充。具体见下表:
消息名
|
传递方向
|
解释说明
|
CMPP_CONNECT
|
SP---
àISMG
|
CMPP_CONNECT操作的目的是SP向ISMG注册作为一个合法SP身份,此消息需要向ISMG发出验证信息,验证方式采用md5加密密码方式,若注册成功后即建立了应用层的连接(否则ISMG会立即断开Socket),此后SP可以通过此ISMG接收和发送短信。
ISMG以CMPP_CONNECT_RESP消息响应SP的请求。具体的算法实现参考CMPP2.0文档和本文附件代码。
|
CMPP_CONNECT_RESP
|
SP
ß---ISMG
|
ISMG对CMPP_CONNECT消息的回复(无论是否验证成功);如果未通过,会在消息中包含参考信息,但ISMG会立即断开连接。
|
CMPP_ACTIVE_TEST
|
SP
ß
àISMG
|
这个消息通信双方都可以发出,目的是在没有其他消息发送时,保持双方的通信链路的连接,避免系统认为通信通道已经关闭。每一个收到此消息的实体应当返回CMPP_ACTIVE_TEST_RESP消息,以“礼节性”表示自己的还在通信,维持数据连接有效性。
不过,据网友交流,有些厂家实现的ISMG,仅仅靠自己发出此消息等待SP回答CMPP_ACTIVE_TEST_RESP来确定数据链路的有效性,而忽略SP的CMPP_ACTIVE_TEST消息(有些霸道吧?)这个值得注意,不要仅仅实现发送而不响应此消息,避免数据连接失效。
|
CMPP_ACTIVE_TEST_RESP
|
SP
ß
àISMG
|
对通信的另一端的CMPP_ACTIVE_TEST消息的回复。作用参考CMPP_ACTIVE_TEST的解释。
|
CMPP_SUBMIT
|
SP---
àISMG
|
在正确建立了数据连接后,SP向ISMG发送一个SMS数据包。本消息需要仔细研究。接收到此消息后,ISMG需要以CMPP_SUBMIT_RESP消息作为回答。如果在一定时间时间内(移动给出的参考值60秒)内未得到消息回应,那么SP需要重新发送此数据包,以确保消息得到投递。如果重发达到3次后仍然得不到回应,SP端应该考虑可能ISMG已经失效,应当停止发送此短消息。
|
CMPP_SUBMIT_RESP
|
SP
ß---ISMG
|
该消息由ISMG发送给SP,同时返回一个“收条”(源CMPP_SUBMIT消息的ISMG端的标示MSGID)给SP,表示“我ISMG已经确认收到你这条消息了”。收到此消息后,SP需要保留此“收条”,因为后面ISMG会最终报告本消息是否正确发送到用户手机。那个报告就是以此消息的“收条”作为确认那一条消息的。
|
CMPP_QUERY
|
SP---
àISMG
|
这个查询不是查询单条消息的,是查询SP发送给ISMG的短信的业务情况。可以查总计数,还可以分类查询。(基本就是发起对移动sms业务数据库的查询统计)
|
CMPP_QUERY_RESP
|
SP
ß---ISMG
|
ISMG将查询的数据返回给SP。
|
CMPP_CANCEL
|
SP---
àISMG
|
SP发起的取消某条消息的命令消息,其中包含了之前已经发送给ISMG消息的“收条”以便ISMG可以确定是那一条消息。如果消息已经发送给用户了,那么此消息/命令会无效,ISMG返回失败。
|
CMPP_CANCEL_RES
|
SP
ß---ISMG
|
ISMG返回的对CMPP_CANCEL的回复,并告知是否删除成功。
|
CMPP_DELIVER
|
SP
ß---ISMG
|
当有MO或者状态报告时,ISMG发送此消息。注意,此消息的数据可以是用户手机发送给SP的消息,也可是对于之前SP发送到ISMG的短信的最终状态的回复,报告短信的最终状态。
|
CMPP_DELIVER_RESP
|
SP---
àISMG
|
SP礼节性的回复告知收到CMPP_DELIVER消息。要指出SP报告的CMPP_DELIVER消息的MSGID,以便ISMG知道那一条消息SP已经确认收到。
|
CMPP_TERMINAT
|
SP
ß
àISMG
|
SP和ISMG都可以主动发消息给对方,自己这端由于某种原因需要终止当前的数据连接。终止后,要经过重新Connection(验证)之后才可以(进入事务阶段)发送SMS数据消息。
|
CMPP_TERMINATE_RES
|
SP
ß
àISMG
|
通知对方,本端已经最好撤除连接的准备。
|
2.
交互阶段
整个CMPP协议交互分为
验证、
事务两个阶段。验证阶段,发送CMPP_CONNECTION消息进行验证,通过验证后(必须要通过才)进入CMPP事务阶段,可以发送短信数据了。上表中的CMPP_CONNECTION以下的消息都属于事务阶段的消息。
3.
消息数据结构
每一个消息包含
消息头 和
消息体两个部分,头固定长度为12字节,其他消息长度各异,但是同一类型消息的长度是固定的。所有消息的各个字段基本上仅有3种类型:Unsigned Integer (无符号整型) 、Integer(整型)、Octet String(字符串),每种类型具体长度不定,网络字节顺序。
1、 消息头(3个Unsigned Integer字段组成):
4字节的Total_Length (Unsigned Integer),包含了此消息的总计(包括了头部分)长度。
4字节的Command_Id(Unsigned Integer),指明了此消息到底是什么消息,就是上表中消息的枚举值。应用程序根据此值确定本数据包到底是什么消息,从而可以按照确定的消息类型,解析余下的消息体。
4字节的Sequence_Id(Unsigned Integer),指明了此数据包在发送此消息端的唯一编号。这个唯一编号,实际上可以看作流水操作编号。因为分析到交互模式我们看到,SP发送数据到ISMG,不是每发送一个就停下来等待ISMG的回复,而是“一下子”发送多个数据包过去,然后等待ISMG的回应。然而,怎么知道回应的消息是到底对应之前发送过去的消息中的那一条呢?本字段就是解决此难题。SP按照编号发送消息过去,等待ISMG的回应—一般情形下回应消息数据结构都有表明本消息回应的是SP发出的哪一条消息,这个对应就是依靠Sequence_Id。它并不要求一定要严格唯一,但是在给定的一段时间内,必须唯一(基本上只要SP发送过去的消息中没有重复就行了)。如果是需要SP回答的消息,SP也必须将ISMG发送过来的消息的Sequence_Id填入相应字段,表明这是某个消息的回应。SP端和ISMG端Sequence_ID都没有确定具体的算法。SP可以(但不推荐)采用数据库的唯一Id作为此值。
2、消息体。消息体长度根据消息不同,长度不一。其他的参考移动的文档《中国移动通信互联网短信网关接口协议(China Mobile Peer to Peer, CMPP)(V2.0)》,这里着重讲讲2个重要消息的消息体数据结构:
CMPP_SUBMIT的消息体:
字段名
|
长度(byte)
|
类型
|
描述
|
Msg_Id
|
8
|
Unsigned Integer
|
信息标识,应该由SP侧ISMG本身产生,本处填空,供ISMG传输时使用。SP提交时候应当留空。
|
Pk_total
|
1
|
Unsigned Integer
|
相同Msg_Id的信息总条数,从1开始。如果一条消息长度超多一条短信,可能需要分解成多条消息,那么实际上这多条消息属于一条完整消息,所以可以根据此给分解得到的多条短信进行编号,那么总计需要编成多少条短信,此处就填写多少。
|
Pk_number
|
1
|
Unsigned Integer
|
相同Msg_Id的信息序号,从1开始。编号决定消息的相对位置。
|
Registered_Delivery
|
1
|
Unsigned Integer
|
是否要求返回状态确认报告:
0:不需要
1:需要
2:产生SMC话单(该类型短信仅供网关计费使用,不发送给目的终端)。
一般情况下,都需要确认报告。SMC话单也需要返回是否成功的报告。这条消息用于包月SMC时,当你发送消息给移动的ISMG,移动的计费系统会一次性扣除用户的信息费,但是此消息不会送到用户手机。但是注意,有的ISMG厂商(很可能是移动要求)实现此消息时候,如果你并没有发送任何此包月类型的消息给用户手机,是不发生扣费行为的。移动会认为这是属于违规的“代收费”行为,会影响同移动的合作关系。
|
Msg_level
|
1
|
Unsigned Integer
|
信息级别,信息的优先级。不过实际当中,感觉ISMG端并没有区分优先级。
|
Service_Id
|
10
|
Octet String
|
业务类型,是数字、字母和符号的组合。这个表示业务的字符串可以给发出的短信分类。通过此字段大约可以知道每个服务项目的业务量,有利于统计和计费以及结算。
|
Fee_UserType
|
1
|
Unsigned Integer
|
计费用户类型字段
0:对目的终端MSISDN计费;
1:对源终端MSISDN计费;
2:对SP计费;
3:表示本字段无效,对谁计费参见
Fee_terminal_Id字段。
|
Fee_terminal_Id
|
21
|
Unsigned Integer
|
被计费用户的号码(如本字节填空,则表示本字段无效,对谁计费参见Fee_UserType字段,本字段与Fee_UserType字段取0、1、2时互斥)
|
TP_pId
|
1
|
Unsigned Integer
|
GSM协议类型。详细是解释请参考GSM03.40中的9.2.3.9
|
TP_udhi
|
1
|
Unsigned Integer
|
GSM协议类型。详细是解释请参考GSM03.40中的9.2.3.23,仅使用1位,右对齐
|
Msg_Fmt
|
1
|
Unsigned Integer
|
信息格式
0:ASCII串
3:短信写卡操作
4:二进制信息
8:UCS2编码
15:含GB汉字
这个决定了Msg_Content字段的字节内容应该按照什么编码来解码/编码。
|
Msg_src
|
6
|
Octet String
|
信息内容来源(SP的企业代码),例如919000。
|
FeeType
|
2
|
Octet String
|
资费类别
01:对“计费用户号码”免费
02:对“计费用户号码”按条计信息费
03:对“计费用户号码”按包月收信息费
04:对“计费用户号码”的信息费封顶
05:对“计费用户号码”的收费是由SP实现。
通常值为02,注意这是一个字符串,并非整型。
|
FeeCode
|
6
|
Octet String
|
资费代码(以分为单位),如:“
0050
”代表人民币
0.50
元。
|
ValId_Time
|
17
|
Octet String
|
存活有效期,格式遵循SMPP3.3协议
|
At_Time
|
17
|
Octet String
|
定时发送时间,格式遵循SMPP3.3协议。这个字段可以让短信在规定的时间给手机用户。一般情况下不填,保留为空字符串。
|
Src_Id
|
21
|
Octet String
|
源号码
SP的服务代码或前缀为服务代码的长号码, 网关将该号码完整的填到SMPP协议Submit_SM消息相应的source_addr字段,该号码最终在用户手机上显示为短消息的主叫号码。实际上就是服务代码,可以是长号码
|
DestUsr_tl
|
1
|
Unsigned Integer
|
接收信息的用户数量(小于100个用户),通常是1。移动是忌讳一条消息发给多个用户的。
|
Dest_terminal_Id
|
21*DestUsr_tl
|
Octet String
|
接收短信的MSISDN号码,一个类似字符串数组的结构。受DestUsr_tl的约束,决定了本字段的长度。
|
Msg_Length
|
1
|
Unsigned Integer
|
信息长度(Msg_Fmt值为0时:<160个字节;其它<=140个字节)。如果是ASCII码,可以达到160个英文字母。原因是因为英文字母仅占用7bit,而中文等双字节代码需要16位,同时每一个字节最高为都占用,所以最多140个字节,也就是70个汉字。
|
Msg_Content
|