短信是指用户通过手机或其他终端设备发送或者接收的文字或者数字信息,是ShortMessageService的简称。一般长度最大为140个英文或数字字符、70个中文字符。
短信主要通过蜂窝无线网络进行传输,一般情况下短信不需要互联网连接即可发送,其通过基站将信息传递到SMS中心(即SMSC)。SMSC将收到的短信转发到接收设备附近的基站。最后基站将信息发送到接收设备。从上可以看出短信主要依靠蜂窝无线网络,不依赖互联网。
短信数据流向框架如下图。
数据流向说明:
2G时代采用比较复杂的CS/PS域架构,SMS(短信)作为上个世纪的产物,一直寄居在2G的CS域架构下。2G/3G时代网络部署的短信网元为SMSC,主要用于短信的存储和转发,在各运营商中数量众多。
到LTE时代,LTE核心网已经没有了短信业务单元,LTE核心网MME单元和2G的MSC服务器单元通过SGs进行建连,进而实现短信功能。
短信功能实现分为三类:
如下图2-1所示,在2G/3G时代,UE设备通过短信收发机将数据最终发送到SMSC,SMSC将收到的信息转发出去,以此完成短信通信。
如今4G短信网络框架图,如下图2-2所示,UE将短信数据发送到eNodeB,eNodeB将短信数据发送到SMSC,SMSC再将数据发送到接收端。两者的区别在于2G/3G和4G网元结构不同,4G网元以及网络结构在这方面比2G/3G要分类更多些,其通过新建网元IP-SM-GW实现SMS短信收发功能。
目前发送短信消息分为:TEXT模式、块模式和PDU模式。
短信编码方式有:GSM编码方式,IRA8bit编码方式,UCS2编码方式。
由于单条短信底层最多支持140个字节,每个字节8bit。由此可知计算单条短信可发送最大字符数据公式如下:
短信内容最大字符数=140*8/E(E-多少个比特位表示一个字符数据)从上述可知:
GSM编码方式E=7,短信内容最大字节数=140*8/7=160IRA编码方式E=8,短信内容最大字节数=140*8/8=140UCS2编码方式E=16,短信内容最大字节数=140*8/16=70由于QuecPython未开放IRA编码方式,目前仅支持GSM,UCS2编码方式,以下只介绍GSM编码短信和UCS2编码短信。
GSM编码也叫GSM7-bit编码,利用7个bit来表示一个字符,键盘键入字符为标准的ASCII码,ASCII码是8比特数据。在实际发送数据时,只有GSM编码对应的字符值和ASCII编码对应的字符值是相同的才能正确的接收数据,否则会造成发送和接收的数据不一致的情况。GSM编码对应的数值不是ASCII码对高位简单的舍弃,需要自行进行鉴别。GSM7bit编码表参考3GPPTS03.38。
UCS2编码用于发送Unicode字符,一条短信最多可发送70字符。UCS2编码可用于发送中文、英文等各种国家的字符。对于字符数值和UCS2编码对应值有标准的编码规定,可自行查询资料。
短信可以分为TEXT短信,PDU短信和块短信。TEXT简单易实现,但在某些场景中并不能满足需求。PDU短信,实现比较复杂,需要转码,但基本能满足绝大部分场景需求。块短信已经很少用,QuecPython目前不支持此种模式,以下不再介绍此模式。
QuecPython接口发送TEXT短信不需要将TEXT短信内容再进行拆解组合进行发送,只需直接写入发送内容即可,短信内容超过140个字节,QuecPython模组将自动将其转为长短信分为多条短信进行发送(表3-1标注最大字节数超过140个字节的型号)。
在实际发送短信时,键入的数据均为ASCII码,而ASCII码是8比特位字符数据,GSM编码是7比特位字符数据,GSM编码将ASCII码中的一些控制字符表示成了有实际意义的字符数据。在TEXT模式下,GSM编码数据需要保证7比特位的数据数值和ASCII码表中的数值完全一致,才可通过TEXT方式发送短信数据,否则将导致接收端和发送端数据不一致。
QuecPython接口发送PDU短信不再需要将短信内容转成PDU码,底层已完成字段转码,只需直接写入发送内容即可,短信内容超过140个字节,QuecPython模组将自动将其转为长短信分为多条短信进行发送(表3-1标注支持最大短信条数大于1的情况下)。
PDU(ProtocolDataUint,协议数据单元)模式,需将短信内容转换成PDU数据才能进行发送。QuecPython接口已进行了封装,在实际的使用过程中,只需将短信内容直接写入。当调用接口时会自动将短信内容转换成PDU码进行发送,并自动添加短信头部设置。
对于短信功能,PDU类型分为以下几类:
对于移动终端设备,着重介绍SMS-SUBMIT和SMS-DELIVER。其结构如下:
SMS-SUBMIT和SMS-DELIVER类型PDU串格式公有部分介绍,参考3GPPTS03.40第9章。比较重要的DCS涉及到短信压缩、短信类型(共四种类型,规定ME对各类型短信采取的策略),短信编码类型(GSM/UCS2)等设置请参考GSM03.38第4章。
以下介绍简单介绍SMS-SUBMIT,SMS-DELIVER两种类型,详细内容参考3GPPTS03.40协议文档。
PDU类型对于SMS-SUBMIT,结构图如下:
下图为接收到PDU数据格式,可以按照如下格式解析数据,各个字段数值表示可以参考3GPPTS03.40协议文档。
PDU类型对于SMS-DELIVER,结构图如下:
短信网络协议框架如下图:
用户编辑短信完成后,通过移动终端设备主动发送短信数据,网络层回复CP-ACK,随后回复RP-ACK,最后移动终端设备回复CP-ACK。
如下图:
移动设备先从网络侧接收到短信,之后移动设备终端回复CP-ACK报文到网络,当短信解析完成,移动设备终端再次发送RP-ACK,网络层回复CP-ACK。
短信存储位置一般分为SM、ME和MT存储。
目前QuecPython模组仅支持SM或ME存储,二者选一。对于短信数量统计以及功能支持,默认储存情况见下表3-1。
短信在广播,公益信息传播方面有很大的优势,对于一些大型项目是重要的组成部分,以下着重介绍短信的应用。对于esim卡,下载profile一般不会有短信中心码,需要先查询下是否存在短信中心码,若不存在请咨询运营商获取正确的短信中心码进行设置。
此方法用于注册短信回调函数,当用户设备接收到短信时,会通过注册的回调函数来通知当前短信存储位置,以及短信存储位置的索引值。如下示例,如收到一条长短信,分成两条短信到达设备将触发两次回调函数。
注册回调接口如下:
sms.setCallback(usrFun)示例
#-*-coding:UTF-8-*-#示例importsmsdefsms_callback(args):print("GetSIMID:{}".format(args[0]))print("GetMessageindex:{}".format(args[1]))print("GetMessageStorage:{}".format(args[2]))sms.setCallback(sms_callback)短信中心码短信中心码是运营商提供的短信转发中心号码,需从运营商获取。设备先将短信发送到短信服务中心,短信服务中心转发收到的短信到目的设备所在的基站,基站将收到的短信下发到目的设备。短信中心码设置后,将其存入sim卡中,一张sim卡设置一次即可,设置即生效。如需更改可再次设置。
短信中心码获取接口如下:
sms.getCenterAddr()示例
#-*-coding:UTF-8-*-#示例importsmscenter_numer=sms.getCenterAddr()print("Currentcenternumberis:{}".format(center_numer))设置短信中心码设置短信中心码,一定要保证设置的短信中心码是正确的,否则可能导致短信发送失败。不同国家或地区的短信中心码,请联系当地的运营商进行确认。
短信中心码设置接口如下:
sms.setCenterAddr(addr)示例
#-*-coding:UTF-8-*-#示例importsms#获取当前短信中心码center_numer=sms.getCenterAddr()print("Currentcenternumberis:{}".format(center_numer))#设置短信中心码center_number="8613010112512"sms.setCenterAddr(center_number)短信存储位置短信存储位置物理上分为SIM卡、ME。目前仅支持SIM卡存储或ME存储,二者选一,不可同时选择。
短信存储位置从逻辑上分为读取和删除存储区域、发送和写入存储区域、接收存储区域。需接收存储区域、读取和删除存储区域保持一致,才能对接收到的短信进行读取和删除操作。
获取短信存储位置接口入下:
sms.getSaveLoc()示例
#-*-coding:UTF-8-*-#示例importsmssave_info=sms.getSaveLoc()print("Messagereadanddeletelocation:{}".format(save_info[0]))print("Messagewriteandsendlocation:{}".format(save_info[1]))print("Messagerecvandsavelocation:{}".format(save_info[2]))设置存储位置短信接收和删除以及读取,均需要从正确的短信存储位置进行操作,需正确设置短信存储位置,才能正常的进行功能应用。
短信存储位置设置接口如下:
sms.setSaveLoc(mem1,mem2,mem3)示例
发送TEXT短信,发送短信内容超过140字节,则转为长短信自动拆分为多条短信发送(表3-1标注支持最大字节数大于140字节的型号)。
接口如下:
sms.sendTextMsg(phoneNumber,msg,codeMode)示例
#-*-coding:UTF-8-*-#示例importsmssms.sendTextMsg('18158626517','firsttestMessage','GSM')#TEXT短信示例2,通过变量发送数据data="secondtestmessage"sms.sendTextMsg('18158626517',data,'GSM')发送PDU短信发送PDU短信,若短信内容长度超过140字节,则转为长短信自动拆分为多条短信发送(表3-1标注支持最大字节数大于140字节的型号)。
sms.sendPduMsg(phoneNumber,msg,codeMode)示例
#-*-coding:UTF-8-*-#示例importsms#PDU短信示例1,直接填入需要发送的数据sms.sendPduMsg('18158626517','sendpdumsgbyGSMmode.','GSM')#PDU短信示例2,通过变量发送数据data="secondtestpdumessage"sms.sendPduMsg('18158626517',data,'GSM')获取短信数量在实际使用过程中,需要查询当前短信数量,来判断当前短信存储空间是否已满。此接口查询需要注意当前设置的短信储存空间,返回的短信数量为当前的短信存储空间的短信数量。
获取短信当前接收条数接口如下:
sms.getMsgNums()示例
#-*-coding:UTF-8-*-#示例importsms#获取当前短信存储信息save_info=sms.getSaveLoc()#获取SM存储区域短信数量sms.setSaveLoc("SM","SM","SM")print("Currentsaveinfo:{}".format(save_info))#获取当前短信存储位置的短信数量message_count=sms.getMsgNums()print("Currentmessagecount:{}".format(message_count))#获取ME存储区域短信数量sms.setSaveLoc("SM","SM","ME")message_count=sms.getMsgNums()print("Currentmessagecount:{}".format(message_count))读取短信当用户收到短信通知,可以通过以下两个接口来读取信息。分为TEXT读取短信和PDU方式读取短信。当收到长短信时,TEXT读取短信会无法区分顺序,需用PDU方式读取短信,根据短信接收PDU数据格式进行解析,合并,在完整应用示例代码中有对此的演示。
以TEXT方式读取短信内容:
sms.searchTextMsg(index)示例
#-*-coding:UTF-8-*-#示例importsmsmessage_count=sms.getMsgNums()ifmessage_count>0:message_info=sms.searchTextMsg(0)print("GetMessgeindex0info:{}".format(message_info))读取PDU短信以PDU方式读取短信内容:
sms.searchPduMsg(index)示例
#-*-coding:UTF-8-*-#示例importsmsmessage_count=sms.getMsgNums()ifmessage_count>0:pdu_message=sms.searchPduMsg(0)#读取到PDU短信,需要解码pdu_len=sms.getPduLength(pdu_message)message_info=sms.decodePdu(pdu_message,pdu_len)print("GetMessgeindex0info:{}".format(message_info))删除短信当短信存储空间已满时,可能会导致设备接收不到短信,用户可通过以下接口来删除一些短信,释放短信存储空间。
删除短信接口如下:
sms.deleteMsg(index[,delmode])示例
#-*-coding:UTF-8-*-#示例importsms#-------获取当前短信存储信息,确保删除短信位置不会出错save_info=sms.getSaveLoc()print("Willdeletemessagefrom{}filed!".format(save_info[0][0]))#删除短信索引为0的短信sms.deleteMsg(0)#---------指定删除短信存储区域内指定索引的短信数据,以ME存储区域为例-------#步骤1、设置操作短信存储区域为MEsave_info[0][0]="ME"sms.setSaveLoc(save_info[0][0],save_info[1][0],save_info[2][0])#步骤2、设置需要操作的短信索引值,以索引值0为例index=0sms.deleteMsg(index)删除全部短信接口同上,只做如下示例。
#-*-coding:UTF-8-*-#示例importsms#-------获取当前短信存储信息,确保删除短信位置不会出错save_info=sms.getSaveLoc()print("Willdeleteallmessagefrom{}filed!".format(save_info[0][0]))#删除全部短信sms.deleteMsg(1,4)#---------指定删除短信存储区域内所有的短信数据,以ME存储区域为例-------#步骤1、设置操作短信存储区域为MEsave_info[0][0]="ME"sms.setSaveLoc(save_info[0][0],save_info[1][0],save_info[2][0])#步骤2、删除ME存储区域所有接收到的短信index=0sms.deleteMsg(1,4)应用示例长短信合并示例。
SIM卡变更运营商,需和原本运营商和新变更运营商确定短信中心码,进行重设,否则短信功能不正常。
eSIM卡,在下载profile时,可能运营商不预设短信中心码,此时需要和运营商确定短信中心码。
eSIM卡,更换profile时,会导致短信中心码被清除掉,需要重设短信中心码。
SM和ME短信存储空间均已满,导致短信接收不到,只有清除短信,释放短信存储空间才能正常接收到短信。
接收到短信后未读取短信,此时更换了短信读取和删除存储空间,导致短信读取不到,请更换回接收到短信时的短信读取和删除存储空间,进行短信的读取和删除操作。
短信存储空间为SM,SM存储空间已满的情况下,想保留短信同时也保证短信可以正常接收,可以更换短信接收存储空间为ME。如果ME存储空间也已满,只有删除短信,才能正常接收到短信。
设备在无信号环境中收不到短信,请移动设备到运营商信号覆盖范围内,此时才能进行短信的正常收发操作。
由于科技的不断更新进步,短信发送成本越来越低,其在项目中的应用越来越广。常见应用场景如下:
问题1:QuecPython支持是否支持长短信?如果支持,最大短信长度是多少?
QuecPython有很多类型的模组,其中有支持长短信,也有仅支持单条短信的模组型号,具体信息详情请见表3-1。
问题2:QuecPython发送长短信是如何处理的,是分成多包发送还是直接发送一条?
QuecPython长短信会分解为一条条发送。
问题3:QuecPython收到长短信是如何处理?是分开存储为多条短信,还是自动合并成一条短信存储?
QuecPython收到长短信会分开存储。这种情况下需要用PDU方式读取短信,并从PDU编码中用户数据头部信息中抽取短信参考编号、总条数、当前短信序号来进行自行处理。
问题4:发送端显示短信已经发送成功,但是模组端未接收到短信可能是什么原因?
可能有如下几种情况,用户可按照下面几种情况依次排查:
问题5:模组插入SIM卡后,已经可以正常联网,为什么发送短信失败?