短信开发系列目录:
之前写了短信接收处理的一些内容,今年事情实在太多了,就停顿了这么一大段的时间。接下来会继续完成相关的内容。
今天先写用之前写的短信类库的一个应用,短信接收引擎。可以用在处理一些短信的提醒;作为前面两篇文章的一个实战运用,可以作为一个多线程、委托和事件、串口等方面知识的一个综合运用。
先来分析一下整个程序的流程:
- 启动线程
- 定时运行线程主函数
- 测试串口是否打开,短信猫是否正常工作
- 发送AT命令,获取所有未阅读的短信
- 遍历每条短信,提取其内容保存到临时变量中
- 尝试删除该短信,如果删除成功,则通知订阅者有新的短信
这个短信接收引擎使用非常简单,你可以轻易的应用到你需要的场景中:
1 /// <summary> 2 /// 窗体加载时发生 3 /// </summary> 4 /// <param name="sender"></param> 5 /// <param name="e"></param> 6 private void MainFrm_Load(object sender, EventArgs e) 7 { 8 var engine = new SmsEngine(ConfigSettings.ComName) 9 { 10 QueryInterval = ConfigSettings.QueryInterval 11 }; 12 engine.OnBufferRead += Engine_OnBufferRead;//读取缓冲区内容时触发 13 engine.OnEngineException += Engine_OnEngineException;//引擎异常时触发 14 engine.OnReceivedMessage += Engine_OnReceivedMessage;//有新的短信时触发 15 engine.Start(); 16 17 tvAddr.ExpandAll(); 18 }
以下是整个引擎的代码,需要的同学可以参考。涉及短信解码编码部分,请参考前面两篇文章。
1 //====================================================================== 2 // 3 // copyright (C) 2012 All rights reserved 4 // framework : 4.0 5 // filename : SmsEngine 6 // description : 7 // author : marvin(马非马) 8 // company : Sysu.im.06imis 9 // create time : 2012/9/5 14:19:24 10 // 11 //====================================================================== 12 using System; 13 using System.IO.Ports; 14 using System.Linq; 15 using System.Text; 16 using System.Threading; 17 using System.Threading.Tasks; 18 using Loncomip.Utility.IO; 19 20 namespace Loncomip.Utility.SmsLib 21 { 22 public class SmsEngine 23 { 24 #region Event 25 /// <summary> 26 /// 接收到短信时处理的回调函数 27 /// </summary> 28 public delegate void MsgReceivedHandler(object sender, MsgReceivedEventArgs e); 29 /// <summary> 30 /// 从串口缓冲区中读到任何内容时的回调函数 31 /// </summary> 32 public delegate void ReadBufferHandler(object sender, ReadBufferEventArgs e); 33 /// <summary> 34 /// 短信引擎发生异常时的回调函数 35 /// </summary> 36 public delegate void EngineExceptionHandler(object sender, EngineExceptionEventArgs e); 37 38 /// <summary> 39 /// 40 /// </summary> 41 public class MsgReceivedEventArgs : EventArgs 42 { 43 /// <summary> 44 /// 发送人的号码 45 /// </summary> 46 /// <value>The sender no.</value> 47 public string SenderNo { get; set; } 48 /// <summary> 49 /// 服务中心发送短信的时间 50 /// </summary> 51 /// <value>The service center send time.</value> 52 public DateTime ServiceCenterSendTime { get; set; } 53 /// <summary> 54 /// 短信的内容 55 /// </summary> 56 /// <value>The content.</value> 57 public string Content { get; set; } 58 public string RawBuffer { get; set; } 59 60 public MsgReceivedEventArgs(DateTime time, string no, string content) 61 { 62 ServiceCenterSendTime = time; 63 SenderNo = no; 64 Content = content; 65 } 66 } 67 /// <summary> 68 /// 69 /// </summary> 70 public class ReadBufferEventArgs : EventArgs 71 { 72 public string Buffer { get; set; } 73 public ReadBufferEventArgs(string buffer) 74 { 75 Buffer = buffer; 76 } 77 } 78 /// <summary> 79 /// 80 /// </summary> 81 public class EngineExceptionEventArgs : EventArgs 82 { 83 public Exception Source { get; set; } 84 public string Message { get; set; } 85 86 public EngineExceptionEventArgs(Exception e, string msg) 87 { 88 Source = e; 89 Message = msg; 90 } 91 } 92 93 public event MsgReceivedHandler OnReceivedMessage; 94 public event ReadBufferHandler OnBufferRead; 95 public event EngineExceptionHandler OnEngineException; 96 #endregion 97 98 #region Fields 99 private SerialPort _serialPort; 100 private CancellationTokenSource _cts; 101 private readonly string _comName; 102 #endregion 103 104 #region Construct 105 public SmsEngine(string comName) 106 { 107 _comName = comName; 108 QueryInterval = 5; 109 } 110 111 public SmsEngine(string comName, bool initCancellationTokenSource) 112 { 113 _comName = comName; 114 QueryInterval = 5; 115 116 if (initCancellationTokenSource) _cts = new CancellationTokenSource(); 117 } 118 #endregion 119 120 public int QueryInterval 121 { 122 get; 123 set; 124 } 125 126 /// <summary> 127 /// Starts this instance. 128 /// </summary> 129 public void Start() 130 { 131 _cts = new CancellationTokenSource(); 132 var task = new Task(Run, _cts.Token); 133 task.Start(); 134 } 135 136 /// <summary> 137 /// Stops this instance. 138 /// </summary> 139 public void Stop() 140 { 141 _cts.Cancel(); 142 } 143 144 /// <summary> 145 /// 线程主函数 146 /// </summary> 147 private void Run() 148 { 149 while (!_cts.IsCancellationRequested) 150 { 151 try 152 { 153 GetAllMsg(); 154 } 155 catch (Exception e) 156 { 157 HandelEnginException(e, "线程异常:{0}", e.Message); 158 _cts.Token.WaitHandle.WaitOne(2 * 1000); 159 } 160 finally 161 { 162 _cts.Token.WaitHandle.WaitOne(QueryInterval * 1000); 163 } 164 } 165 } 166 167 /// <summary> 168 /// 获取所有的短信 169 /// </summary> 170 private void GetAllMsg() 171 { 172 OpenAndTestModem(); 173 WriteCmd(SMSUtil.GenCmdGetList(MessageListType.All));//获取所有未阅读的消息 174 175 var result = GetResult(); 176 if (string.IsNullOrEmpty(result)) return; 177 178 var list = SMSUtil.DecodeSMSGetResult(result); 179 if (list == null || !list.Any()) return; 180 181 foreach (var item in list) 182 { 183 try 184 { 185 WriteCmd(SMSUtil.GenCmdDeleteMsg(item.MsgIndex)); 186 if (SMSUtil.IsSendCmdSuccess(GetResult())) 187 {//成功删除之后,才添加到数据库中 188 NLogHelper.Trace("已成功删除短信{0}#{1},将短信添加到数据库中...", item.MsgIndex,item.SenderNo); 189 var handler = OnReceivedMessage; 190 if (handler != null) handler(this, new MsgReceivedEventArgs(item.ServiceCenterTimeStamp, item.SenderNo, item.Message) { RawBuffer = result }); 191 } 192 else 193 { 194 NLogHelper.Warn("删除短信失败{0}#{1}#{2}#{3}失败", item.MsgIndex, item.SenderNo, item.ServiceCenterTimeStamp, item.Message); 195 } 196 } 197 catch (Exception e) 198 { 199 HandelEnginException(e, "处理单条短信:{0}异常:{1}", item.Message, e.Message); 200 continue; 201 } 202 } 203 } 204 205 /// <summary> 206 /// 写入命令,并返回命令是否写入成功 207 /// </summary> 208 /// <param name="cmd">AT命令</param> 209 /// <param name="waitTime">每次写入命令的间隔时间</param> 210 /// <returns>返回命令的执行情况</returns> 211 private void WriteCmd(string cmd, int waitTime = 500) 212 { 213 try 214 { 215 var buffer = Encoding.ASCII.GetBytes(cmd); 216 _serialPort.Write(buffer, 0, buffer.Length); 217 _cts.Token.WaitHandle.WaitOne(waitTime); 218 } 219 catch (Exception e) 220 { 221 HandelEnginException(e, "往串口写入命令异常:{0}", e.Message); 222 } 223 } 224 225 /// <summary> 226 /// 获取串口中的返回值 227 /// </summary> 228 /// <param name="waitTime">The wait time.</param> 229 /// <returns></returns> 230 private string GetResult(int waitTime = 500) 231 { 232 var len = _serialPort.BytesToRead; 233 var result = new StringBuilder(); 234 while (len > 0) 235 { 236 var buffer = new byte[len]; 237 _serialPort.Read(buffer, 0, len); 238 result.Append(Encoding.ASCII.GetString(buffer)); 239 240 _cts.Token.WaitHandle.WaitOne(waitTime); 241 len = _serialPort.BytesToRead; 242 } 243 if (result.Length > 0) 244 { 245 var handler = OnBufferRead; 246 if (handler != null) OnBufferRead(this, new ReadBufferEventArgs(result.ToString())); 247 } 248 return result.ToString(); 249 } 250 251 /// <summary> 252 /// 打开串口并测试串口是否正常工作 253 /// </summary> 254 private void OpenAndTestModem() 255 { 256 while (!_cts.IsCancellationRequested && _serialPort == null) 257 { 258 OpenModem(); 259 } 260 while (!_cts.IsCancellationRequested && !TestModem()) 261 { 262 263 } 264 } 265 266 /// <summary> 267 /// 测试Modem是否在工作 268 /// </summary> 269 /// <returns></returns> 270 private bool TestModem() 271 { 272 if (_serialPort == null) 273 { 274 _cts.Token.WaitHandle.WaitOne(500); 275 return false; 276 } 277 278 try 279 { 280 WriteCmd("AT\r");//确认串口是否在工作 281 if (!SMSUtil.IsSendCmdSuccess(GetResult())) return false; 282 283 WriteCmd(SMSUtil.GenCmdMsgFormat()); 284 if (!SMSUtil.IsSendCmdSuccess(GetResult())) return false;//设置接收格式为PDU 285 286 return true; 287 } 288 catch (Exception e) 289 { 290 HandelEnginException(e, "测试Modem异常:{0}", e.Message); 291 _cts.Token.WaitHandle.WaitOne(5 * 1000); 292 return false; 293 } 294 } 295 296 /// <summary> 297 /// 打开串口 298 /// </summary> 299 private void OpenModem() 300 { 301 try 302 { 303 _serialPort = new SerialPort 304 { 305 PortName = _comName, 306 BaudRate = 115200, 307 Parity = Parity.None, 308 DataBits = 8, 309 StopBits = StopBits.One, 310 Handshake = Handshake.None, 311 RtsEnable = true, 312 DtrEnable = true 313 }; 314 _serialPort.Open(); 315 } 316 catch (Exception e) 317 { 318 _serialPort = null; 319 320 HandelEnginException(e, "打开串口失败:{0}", e.Message); 321 _cts.Token.WaitHandle.WaitOne(5 * 1000); 322 } 323 } 324 325 /// <summary> 326 /// 处理异常 327 /// </summary> 328 /// <param name="e">The e.</param> 329 /// <param name="msg">The MSG.</param> 330 /// <param name="args">The args.</param> 331 private void HandelEnginException(Exception e, string msg, params object[] args) 332 { 333 var handler = OnEngineException; 334 if (handler != null) 335 { 336 try 337 { 338 if (args.Length > 0) msg = string.Format(msg, args); 339 handler(this, new EngineExceptionEventArgs(e, msg)); 340 } 341 catch (Exception ex) 342 { 343 NLogHelper.Warn("外部异常处理异常:{0}", ex); 344 } 345 346 } 347 NLogHelper.Warn(e.ToString()); 348 } 349 } 350 }