短信开发系列(二):GSM手机短信开发之短信解码

短信开发系列目录:

短信开发系列(一):GSM手机短信开发初探
短信开发系列(二):GSM手机短信开发之短信解码
短信开发系列(三):短信接收引擎

昨天写了短信的发送,今天我们在来谈谈如果读取SIM卡中的短信息,然后将SIM中经过编码的内容进行解码。

首先,我们熟悉几个读取短信的AT命令:

AT+CMGL=0  读取电话上全部未读过的SMS消息
AT+CMGL=2  列出已有的短信息
AT+CMGL=4  读取全部SMS消息
AT+CMGR=X 读取第X条短消息
使用串口工具MCOM,可以调试以上几条命令,具体方法就不谈了。下面谈谈如果完整的读取完缓冲区中所有的短信和如何对短信进行解码
 
1.初始化串口,打开串口
1  var serialPort =  new SerialPort( " COM6 "115200, Parity.None,  8, StopBits.One)
2             {
3                 WriteTimeout =  500,
4                 ReadTimeout =  5000,
5                 RtsEnable =  true,
6                 DtrEnable =  true
7             };
8             serialPort.Open();
2.设置发送命令格式: AT+CMGF=0\r
1 serialPort.Write(SMSUtil.GenSendFormatCmd());
2             Thread.Sleep( 500);
3              var result = ReadBuffer(serialPort);
4             Console.WriteLine(result);
3.读取全部的SMS信息: AT+CMGL=4\r
 
1 serialPort.Write(SMSUtil.GenGetList(MessageListType.All));
2             Thread.Sleep( 500);
3             Console.WriteLine(ReadBuffer(serialPort));
4.读取返回接收缓冲区的内容
这里需要介绍一下接收时的作法,因为之前我试的时候,一直没能接收完整。
 1  ///   <summary>
 2           ///  读取缓冲区的内容
 3           ///   </summary>
 4           ///   <param name="serialPort"> The serial port. </param>
 5           ///   <returns></returns>
 6          private  static  string ReadBuffer(SerialPort serialPort)
 7         {
 8              var len = serialPort.BytesToRead;
 9              var result =  new StringBuilder();
10              while (len >  0)
11             {
12                  var buffer =  new  byte[len];
13                 serialPort.Read(buffer,  0, len);
14                 result.Append(Encoding.ASCII.GetString(buffer));
15 
16                 Thread.Sleep( 500);
17                 len = serialPort.BytesToRead;
18             }
19              return result.ToString();
20         }
上面这段代码,采用了循环读取缓冲区的作法,直到返回的字节数为0,则判断当前已经把缓冲区的所有短信读取完毕。这里要注意,在进行下一次读取时,一定要先休眠500毫秒或者更多,否则此时缓冲区中的字节数为0,嗯,这里没时间研究,不知道是什么原因,求大神指点。当然,应该不是缓冲区不足的原因。之前我没有休眠直接读取时,第一次的返回都只能读取一条短信,不超过200字节,然后重新打开串口,就能返回之后的所有短信内容。
当然,上面的代码是阻塞式的,效率肯定不高,但是还没想到怎么优化。
返回的内容如下:

AT+CMGF=0
OK
AT+CMGL=0
+CMGL: 8,0,,58
0891683108705505F0040D91683115509050F00000217040518530232B207499CD7EB3DA61B93DED66B9DF77103DDD2E83D273900C1693BD6E2F1A2816D3C56A3A180E
+CMGL: 9,0,,57
0891683108705505F0040D91683115509050F00000217040518511232AE8329BFD66B5C3727BDACD72BFEF207ABA5D06A5E720192C267BDD5E34502CA68BD574301C
+CMGL: 10,0,,57
0891683108705505F0040D91683115509050F00000217040518591232AE8329BFD66B5C3727BDACD72BFEF207ABA5D06A5E720192C267BDD5E34502CA68BD574301C

第一行的+CMGL:8,0,,58的格式如下:+CMGL:[当前信息在SIM卡中的索引],[是否已读取],,[返回串的字节长度]

第二行为实际的返回串,包括了信息发送服务中心的号码,发送的时间戳,发送者的号码,发送内容等。具体的解码参考下面的代码

 

 1  ///   <summary>
 2           ///  将接收的ASCII码解析为SMSItem
 3           ///   </summary>
 4           ///   <param name="src"> The SRC. </param>
 5           ///   <returns></returns>
 6          public  static SMSItem DecodeSrc( string src)
 7         {
 8              var item =  new SMSItem();
 9             item.ServiceCenterNo = PopServiceCenterNo( ref src); // 服务中心所在号码
10               // pdu类型
11              var pduType = PopByte( ref src);
12              var bits =  new System.Collections.BitArray( new[] { pduType });
13             item.ReplyPathExists = bits[ 7]; // ?
14             item.UserDataStartsWithHeader = bits[ 6]; // 用户数据区是否具有头部
15             item.StatusReportIndication = bits[ 5]; // ?
16             item.ValidityPeriodFormat = (ValidityPeriodFormat)(pduType &  0x18); // 时间有效性格式
17             item.Direction = (SMSDirection)(pduType &  1); // 当前内容是提交发送的还是接收到的
18 
19              if (item.Direction == SMSDirection.Submited) item.MessageReference = PopByte( ref src); // 如果是提交的,该字节为信息类型(TP-Message-Reference)
20             item.SenderNo = PopSenderNo( ref src);
21             item.ProtocolIdentifier = PopByte( ref src); // 协议标识TP-PID
22             item.DataCodingScheme = PopByte( ref src); // 数据编码方案TP-DCS(TP-Data-Coding-Scheme)
23 
24              if (item.Direction == SMSDirection.Submited)
25             { // 如果是提交的信息,则该字节为数据的有效性
26                 item.SetValidityPeriod(PopByte( ref src));
27             }
28              else
29             { // 如果是接收的,则表示信息中心发送短信的时间
30                 item.ServiceCenterTimeStamp = PopDate( ref src);
31             }
32 
33             item.UserData = src; // 未解码的用户数据区
34              if ( string.IsNullOrEmpty(src))  return item;
35 
36              int userDataLength = PopByte( ref src); // 用户数据区长度
37              if (userDataLength ==  0return item;
38 
39              if (item.UserDataStartsWithHeader)
40             {
41                  var userDataHeaderLength = PopByte( ref src);
42                 item.UserDataHeader = PopBytes( ref src, userDataHeaderLength);
43                 userDataLength -= userDataHeaderLength +  1;
44             }
45 
46              if (userDataLength ==  0return item;
47 
48              switch ((SMSEncoding)item.DataCodingScheme & SMSEncoding.ReservedMask)
49             { // 根据不同的编码方案进行解码
50                  case SMSEncoding._7Bit:
51                     item.Message = Decode7Bit(src, userDataLength);
52                      break;
53                  case SMSEncoding._8Bit:
54                     item.Message = Decode8Bit(src, userDataLength);
55                      break;
56                  case SMSEncoding.UCS2:
57                     item.Message = DecodeUCS2(src, userDataLength);
58                      break;
59             }
60 
61              return item;
62         }

下面是上述解码函数中设计的操作方法:

 

  1  #region 解析接收串
  2          ///   <summary>
  3           ///  把Source中的第一个字节(16进制)移除,并转化为Byte类型
  4           ///   </summary>
  5           ///   <param name="source"> The source. </param>
  6           ///   <returns></returns>
  7          private  static  byte PopByte( ref  string source)
  8         {
  9              var b = Convert.ToByte(source.Substring( 02),  16);
 10             source = source.Substring( 2);
 11 
 12              return b;
 13         }
 14 
 15          ///   <summary>
 16           ///  把Source中的指定长度的字节(16进制)移除,并转化为Byte数组
 17           ///   </summary>
 18           ///   <param name="source"> The source. </param>
 19           ///   <param name="length"> The length. </param>
 20           ///   <returns></returns>
 21          public  static  byte[] PopBytes( ref  string source,  int length)
 22         {
 23              var bytes = source.Substring( 0, length *  2);
 24             source = source.Substring(length *  2);
 25 
 26              return GetBytes(bytes,  16);
 27         }
 28 
 29          ///   <summary>
 30           ///  把Source中的服务中心部分的字符串移除,正常的电话号码类型
 31           ///   </summary>
 32           ///   <param name="source"> The source. </param>
 33           ///   <returns></returns>
 34          private  static  string PopServiceCenterNo( ref  string source)
 35         {
 36              var addrLen = PopByte( ref source); // 地址的长度(指示字节个数)
 37              return addrLen ==  0 ?  string.Empty : PopPhoneNo( ref source, addrLen *  2);
 38         }
 39 
 40          ///   <summary>
 41           ///  把Source中的发送者的号码部分的字符串移除,正常的电话号码类型
 42           ///   </summary>
 43           ///   <param name="source"> The source. </param>
 44           ///   <returns></returns>
 45          private  static  string PopSenderNo( ref  string source)
 46         {
 47              int addrLen = PopByte( ref source);
 48 
 49              return (addrLen = addrLen +  2) ==  2
 50                 ?  string.Empty :
 51                 PopPhoneNo( ref source, addrLen + (addrLen %  2));
 52         }
 53 
 54          ///   <summary>
 55           ///  把Source中的指定长度的字符串移除,并转化为正常的电话号码类型
 56           ///   </summary>
 57           ///   <param name="source"> The source. </param>
 58           ///   <param name="length"> The length. </param>
 59           ///   <returns></returns>
 60          private  static  string PopPhoneNo( ref  string source,  int length)
 61         {
 62              var address = source.Substring( 0, length);
 63             source = source.Substring(address.Length);
 64 
 65              var addressType = PopByte( ref address);
 66             address = SwapOddEven(address).Trim( ' F ');
 67 
 68              if ( 0x09 == addressType >>  4) address =  " + " + address;
 69 
 70              return address;
 71         }
 72 
 73          ///   <summary>
 74           ///  把Source中的前面表示时间的字符串移除,并转化为正常的时间格式
 75           ///   </summary>
 76           ///   <param name="source"> The source. </param>
 77           ///   <returns></returns>
 78          private  static DateTime PopDate( ref  string source)
 79         {
 80              var bytes = GetBytes(SwapOddEven(source.Substring( 012)),  10);
 81 
 82             source = source.Substring( 14);
 83 
 84              return  new DateTime( 2000 + bytes[ 0], bytes[ 1], bytes[ 2], bytes[ 3], bytes[ 4], bytes[ 5]);
 85         }
 86 
 87          ///   <summary>
 88           ///  把某10进制或者16进制的字符串转换为byte数组
 89           ///   </summary>
 90           ///   <param name="source"> 字符串 </param>
 91           ///   <param name="fromBase"> 进制数,10或者16 </param>
 92           ///   <returns></returns>
 93          private  static  byte[] GetBytes( string source,  int fromBase)
 94         {
 95              var bytes =  new List< byte>();
 96 
 97              for ( var i =  0; i < source.Length /  2; i++)
 98             {
 99                 bytes.Add(Convert.ToByte(source.Substring(i *  22), fromBase));
100             }
101 
102              return bytes.ToArray();
103         }
104 
105          ///   <summary>
106           ///  Decode8s the bit.
107           ///   </summary>
108           ///   <param name="source"> The source. </param>
109           ///   <param name="length"> The length. </param>
110           ///   <returns></returns>
111          private  static  string Decode8Bit( string source,  int length)
112         {
113              // or ASCII?
114              return Encoding.UTF8.GetString(GetBytes(source.Substring( 0, length *  2),  16));
115         }
116 
117          ///   <summary>
118           ///  Decodes the UCS2.
119           ///   </summary>
120           ///   <param name="source"> The source. </param>
121           ///   <param name="length"> The length. </param>
122           ///   <returns></returns>
123          private  static  string DecodeUCS2( string source,  int length)
124         {
125              return Encoding.BigEndianUnicode.GetString(GetBytes(source.Substring( 0, length *  2),  16));
126         }
127 
128          ///   <summary>
129           ///  Decode7s the bit.
130           ///   </summary>
131           ///   <param name="source"> The source. </param>
132           ///   <param name="length"> The length. </param>
133           ///   <returns></returns>
134          private  static  string Decode7Bit( string source,  int length)
135         {
136              var bytes = GetInvertBytes(source);
137 
138              var temp =  new StringBuilder();
139              foreach ( var b  in bytes) temp.Append(Convert.ToString(b,  2).PadLeft( 8' 0 '));
140              var binary = temp.ToString().PadRight(length *  7' 0 '); // 转换为2进制,不足在右边补0
141 
142             temp.Clear();
143              for ( var i =  1; i <= length; i++) temp.Append(( char)Convert.ToByte(binary.Substring(binary.Length - i *  77),  2));
144 
145              return temp.ToString().Replace( ' \x0 '' \x40 ');
146         }
147 
148          ///   <summary>
149           ///  把字符串(16进制)转换为byte数组,并反转
150           ///   </summary>
151           ///   <param name="source"> The source. </param>
152           ///   <returns></returns>
153          private  static IEnumerable< byte> GetInvertBytes( string source)
154         {
155              var bytes = GetBytes(source,  16);
156             Array.Reverse(bytes);
157              return bytes;
158         }
159          #endregion

 

最后是信息内容的封装类:

 

  1  public  class SMSItem
  2     {
  3          ///   <summary>
  4           ///  服务中心的号码
  5           ///   </summary>
  6           ///   <value> The service center no. </value>
  7          public  string ServiceCenterNo {  getset; }
  8          ///   <summary>
  9           ///  服务中心发送的时间戳
 10           ///   </summary>
 11           ///   <value> The service center time stamp. </value>
 12          public DateTime ServiceCenterTimeStamp {  getset; }
 13          ///   <summary>
 14           ///  发送者的电话号码
 15           ///   </summary>
 16           ///   <value> The sender no. </value>
 17          public  string SenderNo {  getset; }
 18          public  bool ReplyPathExists {  getset; }
 19          ///   <summary>
 20           ///  用户数据区是否以报文头开始
 21           ///   </summary>
 22           ///   <value>
 23           ///       <c> true </c>  if [user data starts with header]; otherwise,  <c> false </c> .
 24           ///   </value>
 25          public  bool UserDataStartsWithHeader {  getset; }
 26          public  bool StatusReportIndication {  getset; }
 27          public TimeSpan ValidityPeriod {  getset; }
 28          ///   <summary>
 29           ///  时间有效性格式
 30           ///   </summary>
 31           ///   <value> The validity period format. </value>
 32          public ValidityPeriodFormat ValidityPeriodFormat {  getset; }
 33          ///   <summary>
 34           ///  当前报文是提交发送的还是接收到的
 35           ///   </summary>
 36           ///   <value> The direction. </value>
 37          public SMSDirection Direction {  getset; }
 38 
 39          public  byte MessageReference {  getset; }
 40          public  byte ProtocolIdentifier {  getset; }
 41 
 42          ///   <summary>
 43           ///  用户数据编码格式
 44           ///   </summary>
 45           ///   <value> The data coding scheme. </value>
 46          public  byte DataCodingScheme {  getset; }
 47          ///   <summary>
 48           ///  用户数据头
 49           ///   </summary>
 50           ///   <value> The user data header. </value>
 51          public  byte[] UserDataHeader {  getset; }
 52          ///   <summary>
 53           ///  用户数据(未解码)
 54           ///   </summary>
 55           ///   <value> The user data. </value>
 56          public  string UserData {  getset; }
 57          ///   <summary>
 58           ///  用户短信的内容
 59           ///   </summary>
 60           ///   <value> The message. </value>
 61          public  string Message {  getset; }
 62 
 63          #region Methods
 64          ///   <summary>
 65           ///  设置时间验证格式
 66           ///   </summary>
 67           ///   <param name="v"> The v. </param>
 68          public  void SetValidityPeriod( byte v)
 69         {
 70              if (v >  196) ValidityPeriod =  new TimeSpan((v -  192) *  7000);
 71              else  if (v >  167) ValidityPeriod =  new TimeSpan((v -  166),  000);
 72              else  if (v >  143) ValidityPeriod =  new TimeSpan( 12, (v -  143) *  300);
 73              else ValidityPeriod =  new TimeSpan( 0, (v +  1) *  50);
 74         }
 75          ///   <summary>
 76           ///  设置时间验证格式
 77           ///   </summary>
 78           ///   <param name="v"> The v. </param>
 79          public  void SetValidityPeriod(TimeSpan v)
 80         {
 81              if (v.Days >  441)
 82                  throw  new ArgumentOutOfRangeException( " TimeSpan.Days ", v.Days,  " Value must be not greater 441 days. ");
 83 
 84              if (v.Days >  30// Up to 441 days
 85                 SetValidityPeriod(( byte)( 192 + v.Days /  7));
 86              else  if (v.Days >  1// Up to 30 days
 87                 SetValidityPeriod(( byte)( 166 + v.Days));
 88              else  if (v.Hours >  12// Up to 24 hours
 89                 SetValidityPeriod(( byte)( 143 + (v.Hours -  12) *  2 + v.Minutes /  30));
 90              else  if (v.Hours >  1 || v.Minutes >  1// Up to 12 days
 91                 SetValidityPeriod(( byte)(v.Hours *  12 + v.Minutes /  5 -  1));
 92              else
 93             {
 94                 ValidityPeriodFormat = ValidityPeriodFormat.FieldNotPresent;
 95                  return;
 96             }
 97 
 98             ValidityPeriodFormat = ValidityPeriodFormat.Relative;
 99         }
100          #endregion
101     }

 

转载于:https://www.cnblogs.com/marvin/archive/2012/07/05/SMSProgramAndSMSDecode.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值