短信发送原理

短信的原理和实现方法,重点说明了短信的编码方式、AT指令以及用 C#实现串口通讯的方法。

前言

目前,主有三种发送短信的方式:

1、 网关方式:就是向当地的电信部门申请,不需要额外的设备,适用于大型的通信公司,像华为、傲天、中兴、亚信等。

2、 终端方式:就是借助像GSM MODEM之类的设置(支持AT指令的手机也行),通过数据线连接电脑,来发送短信,用这种方法比较适用于小型及个人。要实现这种方式必须理解串口通信、AT指令、短信编码、解码,这也是本文讨论的重点。

3、 利用一些网站来实现,方式简单,不过对网站依赖性太高,对网络的要求也比较高,非常不适于进行项目开发

原理篇

短信编码

在收发短信方面,按时间产生先后,共产生了三种模式:Block Mode、基于AT指令的Text Mode、基于AT指令的PDU Modem, Text Mode比较简单,多款诺基亚手机均支持该模式。西门子的手机大多只支持PDU模式,PDU模式是发送或接收手机SMS信息的一种方法,短信息正文经过十六进制编码后被传送。目前,PDU已取代Block Mode,因我们主要探讨PDU模式的发送。以西门子3508手机为例。

SMS是由Etsi所制定的一个规范(GSM 03.40 和 GSM 03.38)。当使用7-bits编码时,它可以发送最多160个字符;但用8 -bit编码,最多可以发送140个字符,通常无法直接通过手机显示;还有用16-bit编码时,最多70个字符,被用来显示Unicode(UCS2) 文本信息,可以被大多数的手机所显示。我们今天讨论的是UCS2编码,也就是说,最多只能发送70个字符,不管英文还是中文。

现例如我们现在要发送如下信息,向我的手机137xxxxxxxx发送"你好,Hello!"。在没有发送之前,你要清楚,手机SIM卡所在地的短信中心号,并不是你现在所在地方的短信中心号,像我在深圳,深圳的短信中心号是:861xxxxxxxx,即使我现在到外地,短信中心号仍是深圳。从上面我们得到了下面的信息: 接收的手机号:137xxxxxxxx 短信中心号:861xxxxxxxx 短信内容:你好,Hello!

在实际使用中,上面这些信息并不为手机所执行,要进行编码手机才会执行,先不管,看看编码后的信息: 0891683108705500F011000D91683117352446F2000800124F60597DFF0C00480065006C006C006F0021 看不懂吧,我来解释一下:

08 - 指的是短信中心号的长度,也就是指(91)+( 683108705500F0)的长度

91 - 指的是短信息中心号码类型。91是TON/NPI遵守International/E.164标准,指在号码前需加'+'号;此外还有其它数值,但91最常用。 683108705500F0 - 短信息中心号码。由于位置上略有处理,实际号码应为:861xxxxxxxx(字母F是指长度减1)。这需要根据不同的地域作相应的修改。前面的(08)+(91)+( 683108705500F0)实际上就构成了整个短信的一部份,通称短消息中心地址(Address of the SMSC)。

11 - 文件头字节

00 - 信息类型(TP-Message-Reference)

0D - 被叫号码长度

91 - 被叫号码类型

其实在实际处理中,我们通常把11000D91写死在程序中,因为在国内,这些数据都

是不会改变的。

683117352446F2 -被叫号码,经过了位移处理,实际号码为"86137xxxxxxxx"。上面的

(00)+(0D)+(91)+ ( 683117352446F2),构成了整个短信的第二部份目的地址(TP-Destination-Address)。

00 - 协议标识TP-PID,这里一般为00

08 - 数据编码方案TP-DCS(TP-Data-Coding-Scheme),采用前面说的USC2(16bit)数据

编码

00 - 有效期TP-VP(TP-Valid-Period) 12-长度TP-UDL(TP-User-Data-Length),也就是4F60597DFF0C00480065006C006C的长度 36 / 2 = 18 的十六进 12 4F60597DFF0C00480065006C006C 006F0021- 这里就是短信内容了,实际内容为:"你好,Hello!" AT指令

说到AT指令可多了,有厚厚的一本书,不属于我们今天讨论的范围,在这里我仅讨论在发送短信中必须要用的几个AT指令。

与SMS有关的GSM AT指令(from GSM07.05)如表1所示: AT 指令 功 能

AT+CMGC Send an SMS command(发出一条短消息命令)

AT+CMGD Delete SMS message(删除SIM卡内存的短消息)

AT+CMGF Select SMS message formate(选择短消息信息格式:0-PDU;1-文本)

AT+CMGL List SMS message from preferred store(列出SIM卡中的短消息PDU/text: 0/"REC UNREAD"-未读,1/"REC READ"-已读,2/"STO UNSENT"-待发,3/"STO SENT"-已发,4/"ALL"-全部的)

AT+CMGR Read SMS message(读短消息)

AT+CMGS Send SMS message(发送短消息)

AT+CMGW Write SMS message to memory(向SIM内存中写入待发的短消息)

AT+CMSS Send SMS message from storage(从SIN|M内存中发送短消息)

AT+CNMI New SMS message indications(显示新收到的短消息)

AT+CPMS Preferred SMS message storage(选择短消息内存)

AT+CSCA SMS service center address(短消息中心地址)

AT+CSCB Select cell broadcast messages(选择蜂窝广播消息)

AT+CSMP Set SMS text mode parameters(设置短消息文本模式参数)

AT+CSMS Select Message Service(选择短消息服务) 表一:相关的GSM AT指令 我现在以实例来说明这些指令的使用方法: 先用手机数据线将手机连接到电脑

串口,并将串口的波特率设置为19200,可以开始了。

1、首先测试你的连接及手机是否支持AT指令,请在你的串口调试程序中输入: AT<回车> 屏幕上返回"OK"表明计算机与手机连接正常,那样我们就可以进行其它的AT指令测试了

2、设置短信发送格式 AT+CMGF=1<回车> 屏幕上返回"OK"表明现在短信

的发送方式为PDU方式,如果是设置为TEXT方式,则,AT+CMGF=0<回车>

发3、 发送短信 发送内容及手要号仍旧同上面在编码中的一样,编码后,得到要送的数据如下 0891683108705505F011000D91683117352446F2000800124F60597D002C00480065006C006C006F0021

我们用如下指令来发送 AT+CMGS=33<回车> 如果返回">",就把上面编码

数据输入,并以CTRL+Z结尾,稍等一下,你就可以看到返回OK啦。 说明一下,为什么AT+CMGS=33呢,是这样得来的:

11000D91683117352446F2000800124F60597D002C00480065006C006C006F0021 这一段字符串的长度除以2得到的结果,上面的字符串,短信中心号加上短信内容得到的,怎么得到的,请回顾一下解码部份

在我们前面的讨论中,一条完整的短信发送,只要执行三条AT指令,AT、AT+CMGS=?、AT+CMGS=?就可以了。由于篇幅,我只能在这里提到这么多,大家要是想了解更多,可以向各手机厂商索取AT指令白皮书,里面很详细的。

上面讲到的,只能为我们实际中作准备,我们还必须要一个发送途径,根据我们的需要,我们选择投资最少,实现比较方便的串口通信。注意,串口通过数据线跟手机相连,用AT指令来实现发送短信,在我们选择数据线时,建议购买原厂所配,非原厂所配,在使用过程中,经常出现一些莫明其妙的问题,比如,手机屏幕黑了,手机老是提示电池电量不足之类的。

串口通信 在C#中要实现串口通信,很多人都不知所措,在论坛上经常可以看到"怎么用MSCOMM实现串口通信"、"怎样能过串口与设备相连"诸如此类的问题。其实国外的网友早就把这些列入FAQ中了。

通常,在C#中实现串口通信,我们有四种方法:

第一:通过MSCOMM控件这是最简单的,最方便的方法。可功能上很难做到控制自如,同时这个控件并不是系统本身所带,所以还得注册,不在本文讨论范围。可以访问/tutorials/tutorial_details.aspx?tutorial_id =320 ,一个国外网友的写的教程,作者很热心,我曾有发邮件给他,很快就回复了。

细第二:微软在.NET新推出了一个串口控件,基于.NET的P/Invoke调用方法实现,详的大家可以访问微软网站http: ///msdnmag/issues/02/10/NETSerialComm/default.aspx,方便得到更多资料。

第三:就是用第三方控件啦,可一般都要付费的,不太合实际,不作考虑

第四:自己用API写串口通信,这样难度高点,但对于我们来说,可以方便实现自己想要的各种功能

在本文,我们采用第四种方法来实现串口通信,不过不是自己写,用一个国外网友现成的已经封装好的类库,不过功能简单点,相对我们来说已经够用了。

在整个终端短信的操作过程中,与串口的通信,只用到了四个功能,打开、写、读、关闭串口。下面是类库对这四个功能的定义: 打开串口:

函数原型:public void Open() 说明:打开事先设置好的端口

示例: using JustinIO; static JustinIO.CommPort ss_port = new JustinIO.CommPort(); ss_port.PortNum = COM1;

//端口号 ss_port.BaudRate = 19200;

//串口通信波特率 ss_port.ByteSize = 8;

//数据位 ss_port.Parity = 0;

//奇偶校验 ss_port.StopBits = 1;

//停止位 ss_port.ReadTimeout = 1000;

//读超时 try { if (ss_port.Opened) { ss_port.Close(); ss_port.Open(); //打开串口 } else { ss_port.Open();//打开串口 } return true; } catch(Exception e) { MessageBox.Show("错误:" + e.Message); return false; }

写串口: 函数原型:

public void Write(byte[] WriteBytes) WriteBytes 就是你的写入的字节,注意,字符串要转换成字节数组才能进行通信

示例: ss_port.Write(Encoding.ASCII.GetBytes("AT+CGMI\r"));

//获取手机品牌

读串口: 函数原型:

public byte[] Read(int NumBytes) NumBytes 读入缓存数,注意读取来的是字节数组,要实际应用中要进行字符转换

示例: string response = Encoding.ASCII.GetString(ss_port.Read(128)); //读取128个字节缓存

关闭串口:

函数原型:ss_port.Close()

示例: ss_port.Close(); 由于篇幅,以及串口通信涉及内容广泛,我在这里只讲这些。

1 短信协议

1.1 分发中心接口

分发中心接口分为TCP接口和HTTP接口协议,都用的是文本指令方式,也就是说每条指令都是以回程换行结束的,服务端只有接收到回程换行才会进入指令处理。

为了安全,无论TCP或HTTP协议,都要求合作方给出接入的IP地址,没有固定IP地址的合作方需向业务申明。

指令规则:

1、 每条指令分为指令名和指令体,指令名和指令体之间以空格分隔,格式如下: 指令名 (空格) 指令体

2、 每个指令体又由很多个参数赋值语句构成,各参数赋值语句间以&符号分隔,格式如下: 参数赋值语句1&参数赋值语句2&参数赋值语句3 ??? ???

3、 每个参数赋值语句由一个参数名和一个参数值组成,中间用=或:=分隔,用:=分隔表示该参数的值是经过加码处理的,格式如下:

参数名=值 或 参数名:=加码后的值

4、 加码解码的规则如下:

加码时将字符串中的所有字符转换成其对应的ASCII值的16进制值,例如:“A”的ASCII码值为65,以16进制值表示为41,故应发送两个字符“41”以代表字符“A”。示例:msg:=41代表msg=A

对于汉字则以其内码的16进制值来表示,如“测试”应为:B2E2CAD4。示例:msg:= B2E2CAD4表示的是msg=测试 内容来自sql163

参数中只要参数标识与内容之间用“:=”连接的都需要解码后方可使用,解码时将每两位当成其ASCII值的16进制值将其还原。

1.1.1 TCP接口协议

服务端地址1:211.154.164.235 端口号:8020

服务端地址2:211.154.164.236 端口号:8020

Tcp协议适合发送量大,发送和接收比较及时,对于Tcp客户端由于某些原因一段时间内不能连接到分发中心服务器,所有的发向该客户端的MO短信和报告都会被缓存起来,等该客户端一旦连接,便会很快补发。

建议合作方建立一个发送一个接收2个连接,如果业务量很大,可以申请多于2个的连接,但须向业务申请后方可加连接,系统对于连接数是有限制的,每个合作方没有特殊声明时连接数是3,一般可以建2个连接,一个连接用于缓冲在某些情况下断开不能很好识别的情况。

还有,对于每条连接,我们目前的速度限制是10条/秒,如果你的业务超过每连接每秒10条的限制,可以向业务申请调高每条连接的最高限速。

服务器端要求每连接每分钟都要能从客户端接收到至少一条指令数据,如果超过一分钟没有收到,服务器会向客户端发送一个测试指令,只要合作方回应该指令,那么就认为连接是处于激活状态,如果3分钟内服务端都无法接收到客户端的测试回应,将主动断开连接。客户端程序也应该设置3分钟内不能收到服务器端任何指令将主动断开连接并重新连接。

为了防止分发中心连接负载太重,我们要求任何一个连接因任何原因在断开后20秒内不得连接,20秒后再尝试连接,否则如果在1分钟内超过我们设定的连接次数,我们将限制其在后续10分钟之内不能连接,并向系统管理员告警,由系统管理员根据情况停止该用户的合作帐户。

1.1.1.1 连接登陆指令

连接登陆指令是在客户端成功连接后首先应当而且只能在此时发送的指令:

格式:

Login Name=【注册名】&Pwd=【注册密码】&Type=【注册类型,0:接收和发送;1:接收;2:发送;3:稳定接收;4:稳定发送;默认为0】(回车换行)

如果所有服务注册成功,服务器返回给客户端字符串:

Pass(回车换行)

否则将断开连接。对于一次未连接成功,应至少在20秒以后再重试连接,禁止连续的重试连接。

稳定接收和稳定发送的模式是为了更好的收发而新加协议,在这2个模式下,要求发送一方方保存发送的commandid,并根据回馈的commandid确定哪些接收到了,如果未回馈指令超过一定数目(分发中心的默认数目为该登陆设定的每秒下发数),将暂停发送,等待回馈。这样可以减少网络故障时丢失数据,减少连接缓存溢出导致的单向阻塞。各合作方可以根据情况选择使用。

1.1.1.2 连接测试指令

分发中心为了测试服务方是否连接,会在等待1分钟未收到任何数据发送测试指令(该指令也可由服务方主动发起,但没有必要,因为分发中心会定时测试的):

分发中心或服务方==>ActiveTest CommandId=【命令标识,4字节整型,循环使用】(回车换行)

回应:Received CommandId=【对应于发送时的命令标识】(回车换行)

1.1.1.3 接收用户上行指令

该指令是由分发中心向服务方发送用户的短信请求,格式如下:

分发中心==>Deliver (空格)

CommandId=【命令标识,4字节整型,循环使用】

&GateName=【源网关名】

&ItemId=【节目标识,由服务方申请,业务分配】

&UserNumber:=【用户号码】

&UserNumberType=【0:用户号码为真实号码,1:用户号码为伪码】

&SpNumber=【服务号码,必须以9160开头】

&TP_pId=【GSM协议类型。详细解释请参考GSM03.40中的9.2.3.9】

&TP_udhi=【GSM协议类型。详细解释请参考GSM03.40中的9.2.3.23,仅使用1位,右对齐】 &MsgCode=【短信编码,0:ASCII串;3:短信写卡操作;4:二进制信息;8:UCS2编码;15:含GB汉字;24:UCS2编码闪电短信(Msg<=69个汉字),124:GBK编码闪电短信(Msg<=69

个汉字)】

&Msg:=【短信内容,经加码处理,需解码】

&LinkID=【要求对于该MO引起的MT下发时附带该参数】

&AreaCode=【手机所在地区,格式为:(区号,类型,省份,城市)】

(回车换行)

回应:Received CommandId=【对应于发送时的命令标识】(回车换行)

1.1.1.4 下发短信给用户

服务方向分发中心提交发送的短信内容,格式如下:

服务方==>Submit (空格)

CommandId=【命令标识,4字节整型,循环使用】

&GateName=【目的网关名,该参数为空时默认由分发中心根据手机号码决定目的网关名】 &ItemId=【节目标识,由服务方申请,业务分配】

&SpNumber=【服务号码,以9160开头】

&UserNumber:=【目的用户号码,如果是群发将个号码之间用“,”隔开,注意最多只能有255个群发号码,联通禁止群发】 copyright sql163

&UserNumberType=【0:用户号码为真实号码,1:用户号码为伪码】

&FeeNumber:=【计费号码,短信产生的费用由该号码承担,不填时默认向目的用户号码收费】

&FeeNumberType=【0:计费号码为真实号码,1:计费号码为伪码】

&FeeType=【计费类型,1:免费,2:按条计费,3:定制包月计费(FeeType=3目前只针对联通有效)。默认:2】

&ScheduleTime=【定时发送时间,默认立即发送,格式举例:20xx年09月10日20:08:00为:020910200800】。

&ExpireTime=【短信寿命中止时间,格式举例:021201090508,默认为移动或联通(24小时后)中止时间】

&MtFlag=【*引起MT消息的原因,仅当向联通用户发短信时需要该参数,0-MO点播引起的第一条MT消息;1-MO点播引起的非第一条MT消息; 2-非MO点播引起的MT消息。默认为0】

&ReportFlag=【状态报告标志,0:不需要状态报告;1:无论成功与否都返回状态报告;2:联通或小灵通包月收费(当FeeType=3时,要求该字段值必须是2);3:只有最后出错时要返回状态报告(联通有效,移动同1),默认:0】 本文来自数据大全网

&MsgCode=【短信编码,0:ASCII串;3:短信写卡操作;524:二进制信息;8:UCS2编码;15:含GB汉字;24:UCS2编码闪电短信(Msg<=69个汉字),124:GBK编码闪电短信(Msg<=69个汉字)

&MsgId=【用户自定义消息标识,推荐格式:月日时分秒+6位自递增码,例如:9月23日10:00:03发出的序号为1记录可定义为923100003000001。自定义格式最大不超过20个字符且不能有需加码解码的特殊字符】,如果不需要回馈报告可以不写该参数和ExtData参数。

&ExtData:=【短信扩展数据,服务方短信发送的附加信息,在有报告反馈时会连带该扩展数据反馈给服务方,需加码处理,但加码后不能超过120个字节长度。默认为空串】

相关推荐