我们做项目过程中,一般都会有发送短信的需求。最常见的就是户注册或者登录时发送短信验证码。不同类型的短信发送,我们都可以放到到一张短信表中,然后通过一个定时的作业去执行短信发送。而定时作业的执行,我们就可以通过于Window服务去实现。(本篇只说window服务,具体实现短信发送的可以参考博客:http://www.cnblogs.com/ywl925/archive/2012/11/24/2786105.html)
1、首先我们创建一张短信内容表:
1 CREATE TABLE [dbo].[Message]( 2 [ID] [varchar](36) NOT NULL, --ID 3 [MessageContent] [nvarchar](150) NOT NULL, --短信内容 4 [Mobile] [varchar](11) NOT NULL, --手机号 5 [SendType] [varchar](10) NOT NULL, --类型 6 [SendNum] [int] NOT NULL, --发送次数 7 [SendState] [int] NOT NULL, --发送状态 8 [MsgError] [nvarchar](500) NULL, --错误日志 9 [Enable] [bit] NOT NULL, --数据状态 10 [ModifyDate] [datetime] NOT NULL, --修改时间 11 [CreateDate] [datetime] NOT NULL, --创建时间 12 CONSTRAINT [PK_Message] PRIMARY KEY CLUSTERED 13 ( 14 [ID] ASC 15 )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY] 16 ) ON [PRIMARY] 17 18 GO
简单记录发送短信需要的一些基本信息。以后有需要发送的短信,直接添加到短信表中。
2、创建一个Window服务:
3、实现一个获取短信并发送的函数:
添加一个短信转换类SmsTran,类中只有一个方法Send。主要实现的功能是:
1)从数据库中获取n条发送状态为未发送并且发送次数小于最大发送次数的短信记录。
2)遍历获取到的集合,调用短信发送接口SendSMS,并更新返回的结果状态(成功、失败或者其他)、发送次数和修改时间。
3)更新本次数据集合到数据库。
1 public class SmsTran 2 { 3 private static int MaxSendNum = 99; //最多发送次数 4 private static int MaxGetNum = 50; //每次获取的最大条数 5 6 public static void Send() 7 { 8 using (var db = new DBContext()) 9 { 10 var smsServer = new SendSMS(); 11 var messages = db.Set<Message>().Where(s => s.SendState == 0 && s.SendNum <= MaxSendNum).Take(MaxGetNum).ToList(); 12 messages.ForEach(s => 13 { 14 Logger.Debug(null, string.Format("短信发送:{0}, {1}", s.Mobile, s.MessageContent)); 15 var result = smsServer.Send(s.Mobile, s.MessageContent); 16 if (Convert.ToInt32(result) > 0) 17 { 18 s.SendState = 1; 19 s.SendNum = s.SendNum + 1; 20 s.ModifyDate = DateTime.Now; 21 } 22 else 23 { 24 s.SendState = 2; 25 s.SendNum = s.SendNum + 1; 26 s.MsgError = result; 27 s.ModifyDate = DateTime.Now; 28 } 29 db.SaveChanges();31 }); 32 } 33 } 34 }
这里我采用的微软的EF读写数据。SendSMS类是自己写的一个发送短信帮助类,具体实现的方式可以参考我上面发的博客连接。Logger.Debug是一个简单记录日志的方法,有很多中方式可以实现,例如lognet4。
4、添加一个定时器执行短信发送:
在服务中添加命名空间System.Timer。在OnStart中添加一个定时器,定时器的执行事件就是上面我们添加的发送短信的方法。同时简单记录一下日志。
1 public partial class Service : ServiceBase 2 { 3 private Timer timer; 4 5 private object obj = new object(); 6 7 public Service() 8 { 9 InitializeComponent(); 10 } 11 12 protected override void OnStart(string[] args) 13 { 14 Logger.Debug(null, "短信服务已启动 " + DateTime.Now); 15 timer = new Timer(); 16 timer.Interval = 2000; 17 timer.Elapsed += timer_Elapsed; 18 timer.Enabled = true; 19 } 20 21 void timer_Elapsed(object sender, ElapsedEventArgs e) 22 { 23 lock (obj) 24 { 25 try 26 { 27 SmsTran.Send(); 28 } 29 catch (Exception ex) 30 { 31 Logger.Error(null, "短信服务发送错误!", ex); 32 } 33 } 34 } 35 36 protected override void OnStop() 37 { 38 timer.Enabled = false; 39 Logger.Debug(null, "短信服务已停止 " + DateTime.Now); 40 } 41 }
这里有一点需要注意,定时器Timer执行时间到时,如果你上一次的代码没有执行完,它会另起一个线程去执行。所以有可能我们上一次从数据库中取出来的短信还在发送中,没有更新发送状态到数据库,又被重新取出来重复次发送。所以这里我加了一个锁lock,执行完上一次的代码,才允许继续执行。
5、添加安装程序
在设计窗口中右键添加安装程序,会生成 serviceInstaller和serviceProcessInstaller两个组件。把serviceProcessInstaller的属性account改写为 LocalSystem。同时可以把serviceInstaller的属性ServiceName改写为你自己的服务程序名。
6、安装和卸载
编译后可以把生成的代码拷贝到你需要的地方,例如我在D盘中添加了一个文件夹SmsService用来放置刚才编译后的服务文件。
然后点击 开始,运行中输入cmd,获取命令提示符。输入:
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe D:\SmsService\SmsService.exe
回车执行便可安装。卸载的华命名如下:
cd C:\Windows\Microsoft.NET\Framework\v4.0.30319\installutil.exe /u D:\SmsService\SmsService.exe
大家可以把上面两个脚本命令保存成两个脚本文件Install.bat和Uninstall.bat添加服务文件夹下,已方便执行。
安装后就可以在window服务中看到刚才我们自己添加的服务了。
因为我们在代码中添加了日志记录,所以启动服务后,就可以在文件夹下看到日志了。