短信开发系列(三):短信接收引擎
短信开发系列目录:
短信开发系列(一):GSM手机短信开发初探
短信开发系列(二):GSM手机短信开发之短信解码
短信开发系列(三):短信接收引擎
之前写了短信接收处理的一些内容,今年事情实在太多了,就停顿了这么一大段的时间。接下来会继续完成相关的内容。
今天先写用之前写的短信类库的一个应用,短信接收引擎。可以用在处理一些短信的提醒;作为前面两篇文章的一个实战运用,可以作为一个多线程、委托和事件、串口等方面知识的一个综合运用。
先来分析一下整个程序的流程:
- 启动线程
- 定时运行线程主函数
- 测试串口是否打开,短信猫是否正常工作
- 发送AT命令,获取所有未阅读的短信
- 遍历每条短信,提取其内容保存到临时变量中
- 尝试删除该短信,如果删除成功,则通知订阅者有新的短信
这个短信接收引擎使用非常简单,你可以轻易的应用到你需要的场景中:
View Code
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 }
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 }
以下是整个引擎的代码,需要的同学可以参考。涉及短信解码编码部分,请参考前面两篇文章。
View Code
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 }
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 }