前段时间因为需要,重新有研究下了下短信协议和串口通讯的内容,发现比以前有了更多更深入的认识。以前有又好奇,浅浅的了解了一下协议,然后拿自己的v3i 折腾了好一阵子还是失败,只能模拟串口打打电话,读读短息,一条都发不出去,郁闷…… 。由于工作的需要,这次重新上阵,通过不懈努力,终于搞定一款较为全面的短消息发送程序。下面跟大家分享下学习过程中的知识和经验。
1. 短消息协议
首先,肯定要介绍协议了,否则写什么程序呀,连要传输和接收的内容正确不正确都不知道,还是早早歇下吧。
我们讨论的短消息收发有关的规范主要包括GSM 03.38 、GSM 03.40 和GSM 07.05 。前二者着重描述SMS 的技术实现( 含编码方式) ,后者则规定了SMS 的DTE-DCE 接口标准(AT 命令集) 。
一共有三种方式来发送和接收SMS 信息:Block Mode, Text Mode 和PDU Mode 。
Block Mode 已是昔日黄花,目前很少用了。
Text Mode 是纯文本方式,可使用不同的字符集,从技术上说也可用于发送中文短消息,但国内手机基本上不支持,主要用于欧美地区。
PDU Mode 被所有手机支持,可以使用任何字符集,这也是手机默认的编码方式。Text Mode 比较简单,而且不适合做自定义数据传输,我们就不讨论了。下面介绍的内容,是在PDU Mode 下发送和接收短消息的实现方法。
PDU 串表面上是一串ASCII 码,由‘0’-‘9’ 、 ‘A’-‘F’ 这些数字和字母组成。它们是8 位字节的十六进制数,或者BCD 码十进制数。PDU 串不仅包含可显示的消息本身,还包含很多其它信息,如SMS 服务中心号码、目标号码、回复号码、编码方式和服务时间等。发送和接收的PDU 串,结构是不完全相同的。我们先用两个实际的例子说明PDU 串的结构和编排方式。
例1 发送:SMSC 号码是+8613800250500 ,对方号码是13851872468 ,消息内容是“Hello! ”。从手机发出的PDU 串可以是
08 91 68 31 08 20 05 05 F0 11 00 0D 91 68 31 58 81 27 64 F8 00 00 00 06 C8 32 9B FD 0E 01
对照规范,具体分析:
分段 | 含义 | 说明 |
08 | SMSC 地址信息的长度 | 共8 个八位字节( 包括91) |
91 | SMSC 地址格式(TON/NPI) | 用国际格式号码( 在前面加‘+ ’) |
68 31 08 20 05 05 F0 | SMSC 地址 | 8613800250500 ,补‘F ’凑成偶数个 |
11 | 基本参数(TP-MTI/VFP) | 发送,TP-VP 用相对格式 |
00 | 消息基准值(TP-MR) | 0 |
0D | 目标地址数字个数 | 共13 个十进制数( 不包括91 和‘F ’) |
91 | 目标地址格式(TON/NPI) | 用国际格式号码( 在前面加‘+ ’) |
68 31 58 81 27 64 F8 | 目标地址(TP-DA) | 8613851872468 ,补‘F ’凑成偶数个 |
00 | 协议标识(TP-PID) | 是普通GSM 类型,点到点方式 |
00 | 用户信息编码方式(TP-DCS) | 7-bit 编码 |
00 | 有效期(TP-VP) | 5 分钟 |
06 | 用户信息长度(TP-UDL) | 实际长度6 个字节 |
C8 32 9B FD 0E 01 | 用户信息(TP-UD) | “Hello! ” |
例2 接收:SMSC 号码是+8613800250500 ,对方号码是13851872468 ,消息内容是“你好! ”。手机接收到的PDU 串可以是
08 91 68 31 08 20 05 05 F0 84 0D 91 68 31 58 81 27 64 F8 00 08 30 30 21 80 63 54 80 06 4F 60 59 7D 00 21
对照规范,具体分析:
分段 | 含义 | 说明 |
08 | 地址信息的长度 | 8 个八位字节( 包括91) |
91 | SMSC 地址格式(TON/NPI) | 用国际格式号码( 在前面加‘+ ’) |
68 31 08 20 05 05 F0 | SMSC 地址 | 8613800250500 ,补‘F ’凑成偶数个 |
84 | 基本参数(TP-MTI/MMS/RP) | 接收,无更多消息,有回复地址 |
0D | 回复地址数字个数 | 共13 个十进制数( 不包括91 和‘F ’) |
91 | 回复地址格式(TON/NPI) | 用国际格式号码( 在前面加‘+ ’) |
68 31 58 81 27 64 F8 | 回复地址(TP-RA) | 8613851872468 ,补‘F ’凑成偶数个 |
00 | 协议标识(TP-PID) | 是普通GSM 类型,点到点方式 |
08 | 用户信息编码方式(TP-DCS) | UCS2 编码 |
30 30 21 80 63 54 80 | 时间戳(TP-SCTS) | 2003-3-12 08:36:45 +8 时区 |
06 | 用户信息长度(TP-UDL) | 实际长度6 个字节 |
4F 60 59 7D 00 21 | 用户信息(TP-UD) | “你好! ” |
若基本参数的最高位(TP-RP) 为0 ,则没有回复地址的三个段。从Internet 上发出的短消息常常是这种情形。
注意号码和时间的表示方法,不是按正常顺序顺着来的,而且要以‘F’ 将奇数补成偶数。
2. 编码方式
在PDU Mode 中,可以采用三种编码方式来对发送的内容进行编码,它们是7-bit 、8-bit 和UCS2 编码。
7-bit 编码用于发送普通的ASCII 字符,它将一串7-bit 的字符( 最高位为0) 编码成8-bit 的数据,每8 个字符可“ 压缩” 成7 个;8-bit 编码通常用于发送数据消息,比如图片和铃声等;而UCS2 编码用于发送Unicode 字符。PDU 串的用户信息(TP-UD) 段最大容量是140 字节,所以在这三种编码方式下,可以发送的短消息的最大字符数分别是160 、140 和70 。这里,将一个英文字母、一个汉字和一个数据字节都视为一个字符。
需要注意的是,PDU 串的用户信息长度(TP-UDL) ,在各种编码方式下意义有所不同。7-bit 编码时,指原始短消息的字符个数,而不是编码后的字节数。
8-bit 编码时,就是字节数。UCS2 编码时,也是字节数,等于原始短消息的字符数的两倍。如果用户信息(TP-UD) 中存在一个头( 基本参数的TP-UDHI 为1) ,在所有编码方式下,用户信息长度(TP-UDL) 都等于头长度与编码后字节数之和。如果采用GSM 03.42 所建议的压缩算法(TP-DCS 的高3 位为001) ,则该长度也是压缩编码后字节数或头长度与压缩编码后字节数之和。
比如我们对英文短信“Hello!” 进行编码,将源串每8 个字符分为一组( 这个例子中不满8 个) 进行编码,在组内字符间压缩,但每组之间是没有什么联系的。
3. 串口通讯
在程序调试期间最好用串口模拟程序辅助,除非你的sim 卡不愁扣钱。同时再写一个用来测试的仿真程序,用来测试接收到的PDU 码和发送返回数据。这其中要注意的是两边的串口参数一定要设置相同,否则接收到会是乱码或不可显示字符。至于要设置哪些参数,一般使用中,就波特率、奇偶校验、停止位这几个,不用设置太详细,至于软硬检测就不要管了。然后就是发送和接收数据的周期,这点尤为重要。因为是本机的测试,发送和接受的程序执行很快,但是串口通讯是异步的,再加上是模拟短信猫的处理,当然要预留发送短信的处理时间了,因此,要在发送和接受部分加一个进程等待ThreadSleep ,休眠一会再去读串口返回的数据,这样就不会造成一次数据分几批读出导致的判断错误。
4. 多线程
在程序中可以加入手动发送,即写一条发一条;从列表自动发送,即由外部数据如Excel 导入到列表中,然后自动一条发送;从数据库发送,即从数据库取发送信息,定时发送;如果加入以上功能,就需要用线程来完成,同时注意线程间的同步问题。因为串口只有一个,是共享资源,在多个线程运行时要保证它的锁定,否则发送的数据会乱套,返回的也一样。最简单的办法就是在开启一个线程时判断有无其他线程在使用串口,有则暂停该线程,释放串口,等到当前的线程运行结束后再恢复挂起的线程。
5. 结尾
还可以在本程序的基础上扩充为发送小灵通短信,其实就是将编码部分的11000D91 改为11000+ 对方号码编码后长度的十六进制+81 ,其他不变。
今天就写到这了,希望大家都能顺利进行!