CMPP3长短信的实现

近日为了实现长短信功能费了不少周折,今天总算完成了,趁热记录下来:

长短信指超过140字节(70汉字)的短信,在默认情况下(CMPP3、ANSI或GBK编码),需要手工拆分并分条发送,但是手机端的感受很不好(铃声大作、顺序颠倒等)。网上搜集了一些有用的资料,主要参考如下内容:

1.1.1.1 CMPP_SUBMIT消息定义(SP—>SMG)

字段名
字节数
属性
描述
Msg_Id
8
Unsigned Integer
信息标识。
Pk_total
1
Unsigned Integer
相同Msg_Id的信息总条数,从1开始。
Pk_number
1
Unsigned Integer
相同Msg_Id的信息序号,从1开始。
Registered_Delivery
1
Unsigned Integer
是否要求返回状态确认报告:
0:不需要;
1:需要。
Msg_level
1
Unsigned Integer
信息级别。
Service_Id
10
Octet String
业务标识,是数字、字母和符号的组合。
Fee_UserType
1
Unsigned Integer
计费用户类型字段:
0:对目的终端MSISDN计费;
1:对源终端MSISDN计费;
2:对SP计费;
3:表示本字段无效,对谁计费参见Fee_terminal_Id字段。
Fee_terminal_Id
32
Octet String
被计费用户的号码,当Fee_UserType为3时该值有效,当Fee_UserType为0、1、2时该值无意义。
Fee_terminal_type
1
Unsigned Integer
被计费用户的号码类型,0:真实号码;1:伪码。
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_src
6
Octet String
信息内容来源(SP_Id)。
FeeType
2
Octet String
资费类别:
01:对“计费用户号码”免费;
02:对“计费用户号码”按条计信息费;
03:对“计费用户号码”按包月收取信息费。
FeeCode
6
Octet String
资费代码(以分为单位)。
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个用户)。
Dest_terminal_Id
32*DestUsr_tl
Octet String
接收短信的MSISDN号码。
Dest_terminal_type
1
Unsigned Integer
接收短信的用户的号码类型,0:真实号码;1:伪码。
Msg_Length
1
Unsigned Integer
信息长度(Msg_Fmt值为0时:<160个字节;其它<=140个字节),取值大于或等于0。
Msg_Content
Msg_length
Octet String
信息内容。
LinkID
20
Octet String
点播业务使用的LinkID,非点播类业务的MT流程不使用该字段。

红色部分表示发长短信要更改的字段。
洋红色部分表示发长短信可以更改或者不更改的字段(目前没发现意义所在)

在封装CMPP­_SUBMIT消息的时候需要按照以下协议要求:

TP_udhi :0代表内容体里不含有协议头信息 1代表内容含有协议头信息(长短信,push短信等都是在内容体上含有头内容的)当设置内容体包含协议头,需要根据协议写入相应的信息,长短信协议头有两种:


6位协议头格式:05 00 03 XX MM NN

  •  byte 1 : 05, 表示剩余协议头的长度
  •  byte 2 : 00, 这个值在GSM 03.40规范9.2.3.24.1中规定,表示随后的这批超长短信的标识位长度为1(格式中的XX值)。
  •  byte 3 : 03, 这个值表示剩下短信标识的长度
  • byte 4 : XX,这批短信的唯一标志,事实上,SME(手机或者SP)把消息合并完之后,就重新记录,所以这个标志是否唯 一并不是很 重要。
  • byte 5 : MM, 这批短信的数量。如果一个超长短信总共5条,这里的值就是5。
  •  byte 6 : NN, 这批短信的数量。如果当前短信是这批短信中的第一条的值是1,第二条的值是2。

例如:05 00 03 39 02 01

7位的协议头格式:06 08 04 XX XX MM NN

  • byte 1 : 06, 表示剩余协议头的长度
  • byte 2 : 08, 这个值在GSM 03.40规范9.2.3.24.1中规定,表示随后的这批超长短信的标识位长度为2(格式中的XX值)。
  • byte 3 : 04, 这个值表示剩下短信标识的长度
  • byte 4-5 : XX XX,这批短信的唯一标志,事实上,SME(手机或者SP)把消息合并完之后,就重新记录,所以这个标志是否唯一并不是很重要。
  • byte 6 : MM, 这批短信的数量。如果一个超长短信总共5条,这里的值就是5。
  • byte 7 : NN, 这批短信的数量。如果当前短信是这批短信中的第一条的值是1,第二条的值是2。

例如:06 08 04 00 39 02 01

附Delphi核心代码(Java代码可参考:http://yzyzero.iteye.com/blog/1434581

短信消息结构体定义:

// SP 向 ISMG 提交短信消息结构体
    PCMPP_SUBMIT = ^TCMPP_SUBMIT;
    TCMPP_SUBMIT = packed record
        Msg_Id: Int64;
        Pk_total: Byte;
        Pk_number: Byte;
        Registered_Delivery: Byte;
        Msg_level: Byte;
        Service_Id: array[0..9] of Char;
        Fee_UserType: Byte;
        Fee_terminal_Id: array[0..31] of Char;
        Fee_terminal_type: Byte;
        TP_pId: Byte;
        TP_udhi: Byte;
        Msg_Fmt: Byte;
        Msg_src: array[0..5] of Char;
        FeeType: array[0..1] of Char;
        FeeCode: array[0..5] of Char;
        ValId_Time: array[0..16] of Char;
        At_Time: array[0..16] of Char;
        Src_Id: array[0..20] of Char; 
        DestUsr_tl: Byte; 
        Dest_terminal_Id: array[0..31] of Char; 
        Dest_terminal_type : Byte;
        Msg_Length: Byte;
        Msg_Content: array[0..CMPPMSG_LENGTH - 1] of Byte; //信息内容
        LinkID: array[0..19] of Char;

核心代码:

function Tmainform.My_ByteAdd(src,add:array of Byte;startindex,endindex:Cardinal):TBytes;
var
  dst:TBytes;
  i:Integer;
begin
  SetLength(dst,length(src) + endindex - startindex);
  for i := 0 to length(src) - 1 do
  begin
    dst[i] := src[i];
  end;
  for i := 0 to endindex - startindex - 1 do
  begin
    dst[length(src) + i] := add[startindex + i];
  end;
  Result := dst;
end;

//..省略
if My_CanPackBadWord <> 0 then
AShortMessage := My_PackBadWord(Trim(FieldByName('Msg_Content').AsString))//过滤非法词
else
  AShortMessage := Trim(FieldByName('Msg_Content').AsString);
  //根据短信长度进行封包
  if Length(AShortMessage) > CMPPMSG_LENGTH then //超过CMPPMSG_LENGTH为超长短信
  begin
    UCS := TBigEndianUnicodeEncoding.Create; //uEncoding.pas
    try
      AMsgUCSArr := UCS.GetBytes(PChar(AShortMessage)); //转成UCS2字节数组
      AMsgUCSLen := Length(AMsgUCSArr);//长度
      AMsgCount := Ceil(AMsgUCSLen / (CMPPMSG_LENGTH - 6));//需要拆分条数
      for j := 0 to AMsgCount - 1 do
      begin
        SetLength(TP_udhiHeadArr,6); //初始化报头
        //按协议填充报头
        TP_udhiHeadArr[0] := $05;
        TP_udhiHeadArr[1] := $00;
        TP_udhiHeadArr[2] := $03;
        TP_udhiHeadArr[3] := $0A;
        TP_udhiHeadArr[4] := AMsgCount;
        TP_udhiHeadArr[5] := j + 1;
        if (j <> AMsgCount - 1) then //不为最后一条
        begin
          MsgContent := My_ByteAdd(TP_udhiHeadArr,AMsgUCSArr,j*(CMPPMSG_LENGTH - 6),(j + 1)*(CMPPMSG_LENGTH - 6));
        end
        else //最后一条
        begin
          MsgContent := My_ByteAdd(TP_udhiHeadArr,AMsgUCSArr,j*(CMPPMSG_LENGTH - 6),AMsgUCSLen);
        end;
//..省略

代码中用到转码单元uEncoding.pas是因为我用的Delphi 2007对Unicode编码支持不够好,从Delphi盒子上下载的第三方Encoding单元(http://www.2ccc.com/article.asp?articleid=5090),Delphi 2007以上版本有内置方法。
需要特别注意的是:

  • TP_udhi必须设置为1;
  • Msg_Content必须按TP_udhi协议填写6字节或者7字节的TP_udhi协议头然后加上经过USC2编码的消息内容,并且,由TP_udhi协议头和消息内容体组成的Msg_Content总长度不能超过140个字节;
  • Msg_Fmt必须设置为8,即UCS2编码,否则不支持中文。

除此之外,推荐一个比较好用的短信网关模拟器——“卓越短信网关模拟器工具包v1.8”,可以很方便地调试短信网关。

转载请注明:梧桐树下 » CMPP3长短信的实现

与本文相关文章

发表我的评论

取消评论
表情 插代码

Hi,您需要填写昵称和邮箱!

  • 必填项
  • 必填项