发送短消息常用Text和PDU(Protocol Data Unit,协议数据单元)模式。使用Text模式收发短信代码简单,实现起来十分容易,但最大的缺点是不能收发中文短信;而PDU模式不仅支持中文短信,也能发送英文短信。PDU模式收发短信可以使用3种编码:7-bit、8-bit和UCS2编码。7-bit编码用于发送普通的ASCII字符,8-bit编码通常用于发送数据消息,UCS2编码用于发送Unicode字符。一般的PDU编码由A B C D E F G H I J K L M十三项组成。
举例:08 91 68 31 08 20 05 05 F0 11 00 0D 91 68 31 96 03 29 30 F0 00 00 00 06 C8 32 9B FD 0E 01
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:(1)数据编码方案为8bit时,表示有效期,2位十六进制数。(2)7bit和USC2下,表示时间戳(TP-SCTS)
L:用户数据长度,2位十六进制数。
M:用户数据,其长度由L中的数据决定。J中设定采用UCS2编码,这里是中英文的Unicode字符。
解码代码:
//对短信息进行分析
public synchronized RevSmsVo analyseMessage(String cmtContent) {
RevSmsVo revSmsVo=new RevSmsVo();
//08 91 68 31 08 20 05 05 F0 11 00 0D 91 68 31 96 03 29 30 F0 00 00 00 06 C8 32 9B FD 0E 01
/*
A 08 SMSC地址信息的长度 共8个八位字节(包括91)
B 91 SMSC地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)
C 68 31 08 20 05 05 F0 SMSC地址 8613800250500,补‘F’凑成偶数个
D 11 基本参数(TP-MTI/VFP) 发送,TP-VP用相对格式
E 00 消息基准值(TP-MR) 0
F 0D 目标地址数字个数 共13个十进制数(不包括91和‘F’)
G 91 目标地址格式(TON/NPI) 用国际格式号码(在前面加‘+’)
H 68 31 96 03 29 30 F0 目标地址(TP-DA) 8613693092030,补‘F’凑成偶数个
I 00 协议标识(TP-PID) 是普通GSM类型,点到点方式
J 00 用户信息编码方式(TP-DCS) 7-bit编码
K 00 有效期(TP-VP) 5分钟
L 06 用户信息长度(TP-UDL) 实际长度6个字节
M C8 32 9B FD 0E 01 用户信息(TP-UD) “Hello!”
*/
//A:短信息中心地址长度,2位十六进制数(1字节)。
StringBuffer sb=new StringBuffer(cmtContent.replace(" ",""));
int smsc_len=Integer.parseInt(sb.substring(0,2));
sb.delete(0, 2);
if(smsc_len>0){
//B:短信息中心号码类型,2位十六进制数。
String smsc_format=sb.substring(0,2);
sb.delete(0, 2);
//C:短信息中心号码,B+C的长度将由A中的数据决定。
String smsc_code=changeSimCode(sb.substring(0,(smsc_len*2-2))).replaceFirst("F","");
if("001".equals(Arith.hexString2binaryString(smsc_format).substring(1, 4)))
smsc_code=smsc_code.substring(2);//去国别码,参见TON/NPI标准
sb.delete(0, (smsc_len*2-2));
MyLog.logger.debug("短信解码调试 > 短信中心号码"+smsc_code);
}
//D:文件头字节,2位十六进制数。
String d=sb.substring(0, 2);
sb.delete(0, 2);
//E:信息类型,2位十六进制数。
if("11".equals(d))
sb.delete(0, 2);
//F:被叫号码长度,2位十六进制数。0D 目标地址数字个数 共13个十进制数(不包括91和‘F’)
int simCodeLength= Integer.parseInt(sb.substring(0,2), 16);
if (simCodeLength % 2 != 0)
simCodeLength = simCodeLength + 1;
MyLog.logger.debug("短信解码调试 > 被叫号码长度:"+simCodeLength);
sb.delete(0, 2);
//被叫号码格式,2位十六进制数。
String smsCode_format=sb.substring(0,2);
sb.delete(0, 2);
MyLog.logger.debug("短信解码调试 > 被叫号码格式:"+smsCode_format);
//G:被叫号码类型,2位十六进制数,取值同B。
String simCode=changeSimCode(sb.substring(0,simCodeLength)).replaceFirst("F","");
if("001".equals(Arith.hexString2binaryString(smsCode_format).substring(1, 4)))
simCode=simCode.substring(2);//去国别码,参见TON/NPI标准
revSmsVo.setSimCode(simCode);
sb.delete(0, simCodeLength);
MyLog.logger.debug("短信解码调试 > 被叫号码"+simCode);
//I:协议标识,2位十六进制数。
sb.delete(0, 2);
//J:数据编码方案,2位十六进制数。
int msgFormat= Integer.valueOf(sb.substring(0,2));
sb.delete(0, 2);
MyLog.logger.debug("短信解码调试 > 数据编码方案"+msgFormat+"(0=7Bit,4=8bit,8=SCS2中文)");
//K:7-BIT(有效期,2位十六进制数,FF表示最大),UCS2的时间戳(7对16进制数。即短信中心时间 50208151754500字节反转05/02/18 15:57:45 最后的00代表时区,这里为0)。
int msgLength;
String msgInfo;
switch(msgFormat){
case 0://7bit
//K:时间戳(7对16进制数。即短信中心时间 50208151754500字节反转05/02/18 15:57:45 最后的00代表时区,这里为0)。
sb.delete(0, 14);
//L:用户数据长度,2位十六进制数。
int len = Integer.parseInt(sb.substring(0,2), 16);
//7bit的71个字符,对应字节数71/8+?1,然后成2
msgLength=((int) Math.floor(len*7/8)+1)*2;
MyLog.logger.debug("短信解码调试 > 用户数据长度(字节):"+len+"("+sb.substring(0,2)+"),16进制字符长度"+msgLength);
sb.delete(0, 2);
//M:用户数据,其长度由L中的数据决定。
msgInfo = sb.substring(0,msgLength);
revSmsVo.setSmsContent(decode7bit(msgInfo));
break;
case 4://8bit
//K:8-BIT(有效期,2位十六进制数)
sb.delete(0, 2);
//L:用户数据长度,2位十六进制数。
msgLength = Integer.parseInt(sb.substring(0,2), 16);
sb.delete(0, 2);
//M:用户数据,其长度由L中的数据决定。
msgInfo = sb.substring(0,msgLength*2);
revSmsVo.setSmsContent(decode8bit(msgInfo));
break;
case 8://中文
//K:时间戳(7对16进制数。即短信中心时间 50208151754500字节反转05/02/18 15:57:45 最后的00代表时区,这里为0)。
sb.delete(0, 14);
//L:用户数据长度,2位十六进制数。
msgLength = Integer.parseInt(sb.substring(0,2), 16);
sb.delete(0, 2);
//M:用户数据,其长度由L中的数据决定。J中设定采用UCS2编码,这里是中英文的Unicode字符。
msgInfo = sb.substring(0,msgLength*2);
revSmsVo.setSmsContent(unicode2asc(msgInfo));
break;
default:
break;
}
MyLog.logger.debug("短信解码调试 > 用户数据"+revSmsVo.getSmsContent());
return revSmsVo;
}
附TON/NPI
例如:0X91
地址类型:10010001
Bits 7: 始终为1
Bits 6,5,4:Type-of-Number(号码类型):001,代表Internation Number。也即是号码前加“+”。注意:对某些比较特殊的号码,例如手机与小灵通的互通时,这里不能设置为001,而要设置成000,代表号码前没有“+”,否则无法接收。
下面是GSM03.40协议号码类型的解释:
0 0 0 Unknown
0 0 1 International number
0 1 0 National number
0 1 1 Network specific number
1 0 0 Subscriber number
1 0 1 Alphanumeric(coded according to TS03.38 7-bit default alphabet)
1 1 0 Abbreviated number
1 1 1 Reserved for extension
ll not interpret reserved values but will store them as received.
Bits 3,2,1,0:Numbering-plan-identification(号码鉴别),0000—未知,0001—ISDN/电话号码(E.164/E.163),1111—留作扩展;一般默认为0001,表示电话号码类型的。下面是GSM03.40号码鉴别的解释:
Bits4 3 2 1
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.