Hsienhua's blog

自己的学习之路

手机短信的PDU编码和解码

(一 )  手机发送和接收SMS信息的方式:Block Mode, Text Mode和PDU Mode。其中PDU Mode被所有手机支持,可以使用任何字符集,这也是手机默认的编码方式。


发送短消息常用Text和PDU(Protocol Data Unit,协议数据单元)模式。使用Text模式收发短信代码简单,实现起来十分容易,但最大的缺点是不能收发中文短信;而PDU模式不仅支持中文短信,也能发送英文短信。PDU模式收发短信可以使用3种编码:7-bit、8-bit和UCS2编码。7-bit编码用于发送普通的ASCII字符,它将一串7-bit的字符(最高位为0)编码成8-bit的数据,每8个字符可“压缩”成7个;8-bit编码通常用于发送数据消息,比如图片和铃声等;而UCS2编码用于发送Unicode字符。在这三种编码方式下,PDU串的用户信息(TP-UD)段最大容量(可以发送的短消息的最大字符数)分别是160、140和70。这里,将一个英文字母、一个汉字和一个数据字节都视为一个字符。


(二) PDU编码协议简单说明:

一般的PDU编码由A B C D E F G H I J K L M十三项组成。
A:短信息中心地址长度,2位十六进制数(1字节)。
B:短信息中心号码类型,2位十六进制数。
C:短信息中心号码,B+C的长度将由A中的数据决定。
D:文件头字节,2位十六进制数。
E:信息类型,2位十六进制数。
F:被叫号码长度,2位十六进制数。
G:被叫号码类型,2位十六进制数,取值同B。
H:被叫号码,长度由F中的数据决定。
I:协议标识,2位十六进制数。
J:数据编码方案,2位十六进制数。
K:有效期,2位十六进制数。
L:用户数据长度,2位十六进制数。
M:用户数据,其长度由L中的数据决定。J中设定采用UCS2编码,这里是中英文的Unicode字符。


例子说明:
发送: 0891683108200505F011190D91683105155694F50008FF10008FF044F60597D

<1>短信中心地址字段:0891 
08:Address-Lengt(地址长度),短信息中心地址长度为8个字节,是(91)+(683108200505F0)的长度,8个8位字节
91地址类型:10010001 值含义:    
    Bit7:1。始终为1       

Bits 6,5,4:Type-of-Number(号码类型):001,代表Internation Number。也即是号码前加“+”。注意:对某些比较特殊的号码,例如手机与小灵通的互通时,这里不能设置为001,而要设置成000,代表号码前没有“+”,否则无法接收。


       Bits 3,2,1,0:Numbering-plan-identification(号码鉴别),0000—未知,0001—ISDN/电话号码(E.164/E.163),1111—留作扩展;一般默认为0001,表示电话号码类型的。下面是GSM03.40号码鉴别的解释:
       Bits3 2 1 0
                     0 0 0 0    Unknown
                     0 0 0 1    ISDN/telephone numbering plan (E.164/E.163)
                     0 0 1 1    Data numbering plan (X.121)
                     0 1 0 0    Telex numbering plan
                     1 0 0 0    National numbering plan
                     1 0 0 1    Private numbering plan
                     1 0 1 0    ERMES numbering plan (ETSI DE/PS 3 01-3)
                     1 1 1 1    Reserved for extension
                    All other values are reserved.  

<2>短信中心号码:683108200805F0
一个字节内反转,8613800280500,如果长度为奇数则需要加“F”补齐。比如号码为:+8613505165495,去掉"+"后在末尾添加F变为:8613505165495F,再将手机号码的奇数位和偶数位的相交换为683105155694F5


<3>FirstOctet字段:1119
     (1)11       包含TP-MTI(2bit),TP-RD(1bit),TP-VPF(2bit),TP-RP(1bit),TP-UDHI(1bit),TP-SRR(1bit)二进制表示形式:

0 0 0 10 0 01

     TP-MTI:01 TP-Message-Type-Indicator(消息类型指示符)

         Bit1,0:00—读出(Deliver); 01—提交(Submit)
         Bit1,0:01指示为SMS-SUBMIT类型 


      TP-VPF:10   TP-Validity-Period-Format(有效期格式)

                 Bit4,3::00—不提供(Not present);01—预留;  10—整型(标准),指使用相对格式;11—提供8位字节的一半                                   (Semi-Octet Represented) 

                 Bit5:    1:需要报告,0:不需要报告。

      TP-UDHI:0   TP-User-Data-Header-Indicator(用户数据头标示)   Bit6:   1:含头信息  0:不含头信息,指示这是一个SMS    消息,没有用户数据头,EMS(增强消息业务)消息需要设置。图片铃声这些都是包含头部信息的.文本不包含头部信

       TP-RP:0     TP-Reply-Path(回复路径)   Bit7:    1:设置回复路径,0:没有设置回复路径。



       (2)消息参考值TP-MR (TP-Message-Reference):19如果使用"00" 值代表让电话自己设置消息参考值。
 
 <4> 对方号码字段:0D913105155694F5 
    0D:目标地址数字个数,共13个十进制数(不包括91和‘F’)
    91:地址类型,同短信中心号码设置.
    3105155694F5:目标手机号码。

<5>上层协议标识TP-PID(TP-Protocol-Identifier):00
     一般设置为00,表示普通GSM,点对点 
<6> 数据编码设置TP-DCS(TP-Data-Coding-Scheme):08
 指示TP-UD的编码方式。08代表Unicode方式。
<7>有效期TP-VP(TP-Validity-Period):FF
       FF表示最大。
<8> 用户数据长度TP-UDL(TP-User-Data-Length):4
      用户数据实际长度。注意不同编码下用户长度定义不同。
<9>用户数据 :4F60597D     "你好"的Unicode编码
你:0x4F60;好:0x597D




2:手机接收的PDU串

 0891683108200505F0040D91683105155694F5000850208151754500044F60597D
<1>短信中心地址字段:0891683108200505F0,即是+8613800250500
<2>FirstOctet :04        其二进制代码:00000100
    TP-MTI:00
    TP-MMS(TP-More-Message-to-Send):1 短信中心没有更多的消息发送
    TP-SRI: 0
    TP-UDHI:0
    TP-RP:  0
<3>发送方号码 :0D91683105155694F5   即+8613505165495
<4>协议标识: 00    TP-DCS 点对点
<5>编码方式: 08    TP-DCS Unicode编码
<6>短信中心时间    50208151754500 
     字节反转05/02/18 15:57:45 最后的00代表时区,这里为0
<7>用户数据长度 :4
<8>用户数据:4F60597D     
   中文“你好”的Unicode编码

(三) 7bit编解码的实现:

   压缩方式:第一个压缩后字节是第一个7bit在最高位加上第二个7bit的最低位,第二个压缩字节是第二个7bit的高六位加上第三个7bit的低两位,依次类推。 第七个压缩后字节(最后一个压缩字节)是第七个7bit的最高位加上整个第八个7bit的七位。这样就实现了将8个字节的7bit编码压缩成7个字节的8bit编码。


     举一个具体的例子,字符串3132333435363738是7bit编码,现压缩成8bit编码。 3132333435363738转换为bit为00110001(31) 00110010(32) 00110011(33) 00110100(34) 00110101(35) 00110110(36) 00110111(37) 00111000(38),进行转换,过程如下: a,转换31,32的最低位到31的最高位,可以看到31不变,第一个压缩后字节为31 b,转换32,32由于取了最低位,相当于向右移了一位,为00011001,将33的低两位放在右移一位的31高位上,也就为11011001,即D9。 c,转换33,33由于取了低两位,相当于向右移了两位,为00001100,将34的低三位放在右移两位的33高位上,也就为10001100,即8C。 d,转换34,34由于取了低三位,相当于向右移了三位,为00000110,将35的低四位放在右移三位的34高位上,也就为01010110,即56。 e,转换35,35由于取了低四位,相当于向右移了四位,为00000011,将36的低五位放在右移四位的35高位上,也就为10110011,即B3。 f,转换36,36由于取了低五位,相当于向右移了五位,为00000001,将37的低六位放在右移五位的36高位上,也就为11011101,即DD。 g,转换37,37由于取了低六位,相当于向右移了六位,为00000000,将整个38的七位放在右移六位的37高位上,也就是1110000,即70。 到此7位编码的3132333435363738压缩为了7字节的31D98C56B3DD70,也就是说压缩掉了一字节。

     在进行编码实现时,可以将压缩前的短信每8字节为一组进行处理,处理为压缩后的七字节,这样形成外层循环。循环次数为短信长度除以8再向上取整,例如11/8=1, 1+1 = 2,也就是经过两次循环。进行内层循环处理时,需要进行七次循环,循环到n(0<n<8)次时,首先将当前字节右移n-1位,取后一字节的低n位,放在当前字节右移n-1位后的高n位,这样就可以完成对8字节的压缩,进而完成对整个短信的压缩


7bit->8bit(C语言)

int   gsmEncode7bit(const   char*   pSrc,   unsigned   char*   pDst,   int   nSrcLength);

//   7bit编码
//   输入:   pSrc   -   源字符串指针
//               nSrcLength   -   源字符串长度
//   输出:   pDst   -   目标编码串指针
//   返回:   目标编码串长度
int   gsmEncode7bit(const   char*   pSrc,   unsigned   char*   pDst,   int   nSrcLength)
{
int   nSrc; //   源字符串的计数值
int   nDst; //   目标编码串的计数值
int   nChar; //   当前正在处理的组内字符字节的序号,范围是0-7
unsigned   char   nLeft; //   上一字节残余的数据

//   计数值初始化
nSrc   =   0;
nDst   =   0;

//   将源串每8个字节分为一组,压缩成7个字节
//   循环该处理过程,直至源串被处理完
//   如果分组不到8字节,也能正确处理
while   (nSrc   <   nSrcLength)
{
//   取源字符串的计数值的最低3位
nChar   =   nSrc   &   7;

//   处理源串的每个字节
if(nChar   ==   0)
{
//   组内第一个字节,只是保存起来,待处理下一个字节时使用
nLeft   =   *pSrc;
}
else
{
//   组内其它字节,将其右边部分与残余数据相加,得到一个目标编码字节
*pDst   =   (*pSrc   <<   (8-nChar))   |   nLeft;

//   将该字节剩下的左边部分,作为残余数据保存起来
nLeft   =   *pSrc   >>   nChar;

//   修改目标串的指针和计数值
pDst++;
nDst++;
}

//   修改源串的指针和计数值
pSrc++;
nSrc++;
}

//   返回目标串长度
return   nDst;
}


8bit->7bit:
int   gsmDecode7bit(const   unsigned   char*   pSrc,   char*   pDst,   int   nSrcLength);

//   7bit解码
//   输入:   pSrc   -   源编码串指针
//               nSrcLength   -   源编码串长度
//   输出:   pDst   -   目标字符串指针
//   返回:   目标字符串长度
int   gsmDecode7bit(const   unsigned   char*   pSrc,   char*   pDst,   int   nSrcLength)
{
int   nSrc; //   源字符串的计数值
int   nDst; //   目标解码串的计数值
int   nByte; //   当前正在处理的组内字节的序号,范围是0-6
unsigned   char   nLeft; //   上一字节残余的数据

//   计数值初始化
nSrc   =   0;
nDst   =   0;

//   组内字节序号和残余数据初始化
nByte   =   0;
nLeft   =   0;

//   将源数据每7个字节分为一组,解压缩成8个字节
//   循环该处理过程,直至源数据被处理完
//   如果分组不到7字节,也能正确处理
while(nSrc<nSrcLength)
{
//   将源字节右边部分与残余数据相加,去掉最高位,得到一个目标解码字节
*pDst   =   ((*pSrc   <<   nByte)   |   nLeft)   &   0x7f;

//   将该字节剩下的左边部分,作为残余数据保存起来
nLeft   =   *pSrc   >>   (7-nByte);

//   修改目标串的指针和计数值
pDst++;
nDst++;

//   修改字节计数值
nByte++;

//   到了一组的最后一个字节
if(nByte   ==   7)
{
//   额外得到一个目标解码字节
*pDst   =   nLeft;

//   修改目标串的指针和计数值
pDst++;
nDst++;

//   组内字节序号和残余数据初始化
nByte   =   0;
nLeft   =   0;
}

//   修改源串的指针和计数值
pSrc++;
nSrc++;
}

   




阅读更多
个人分类: 其它
上一篇jsp基本语法
下一篇Python添加自定义模块
想对作者说点什么? 我来说一句

PDUCode

2006年03月16日 84KB 下载

PDU短信编码解析大师 VER 1.55

2006年11月07日 1000KB 下载

没有更多推荐了,返回首页

关闭
关闭