Reminder[短信提示工具]开发碎碎念。。。附带感谢移动[smtp.139.com]提供的免费午餐。。
2010-08-31 22:31 stubman 阅读(2953) 评论(19) 编辑 收藏 举报Reminder是我最近开发的一个股票行情短信提示软件(刚做完,还是不够稳定。。)。开发这个小东东的想法缘起前段时间在首页看到一篇介绍开放Web Service的文章,其中有个Web Service就是获取实时股票行情的,看到后突然有了做个行情短信提示软件的想法,主要也是由于个人需求驱动,接着。。。革命开始了。。
Reminder的基础功能很简单:
1、实时获取股票行情;
2、根据股票行情判断是否需要进行提示;
3、发送短信提示;
可是做起来也挺花时间。。。。接下来逐一介绍吧,这里主要提供思路,代码比较简单,也完成的比较仓促,就不全发了。
1、获取股票行情
虽然做Reminder的想法缘起Web Service,不过最后实现的时候是调用了新浪的股票行情接口, 之所以选用后者处于两方面的原因:第一,出于稳定性的考虑,觉得新浪比较靠谱。。第二,个人感觉使用更方便一点,并且一次访问可以提取多个股票行情,配置多只股票的话,访问次数少。代码很简单:
2 /// 发出http get请求 获取返回的信息
3 /// </summary>
4 /// <param name="url">例:http://hq.sinajs.cn/list=sh0000001</param>
5 /// <returns></returns>
6 public string HttpGet(string url)
7 {
8 string[] list = new string[2];
9 HttpWebRequest request = null;
10 HttpWebResponse response = null;
11 string responseHTML = string.Empty;
12
13 request = (HttpWebRequest)WebRequest.Create(url);
14 request.Method = "GET";
15 //返回HTML
16 response = (HttpWebResponse)request.GetResponse();
17 Stream dataStream = response.GetResponseStream();
18 StreamReader reader = new StreamReader(dataStream, Encoding.GetEncoding("gb2312"));
19 responseHTML = reader.ReadToEnd();
20
21 return responseHTML;
22 }
得到返回的HTML之后当然就是进行处理,这里只贴出返回数据的格式,具体应用中要如何定义数据结构请自便。。
/// 解析从 WebRequest返回的HTML数据,将其处理成为StockInfo对象
/// </summary>
/// <param name="stockHtml">
/// 返回的HTML:
///var hq_str_sh601003 = " 柳钢股份,5.04,5.05,5.24,5.50,4.96,5.24,5.25,16079772,84917716,11047,5.24,
/// 53100,5.23,30300,5.22,24400,5.21,93000,5.20,54500,5.25,44600,5.26,48943,5.27,41154,5.28,40170,
/// 5.29,2010-08-24,15:02:07";
///
///0:”大秦铁路”,股票名字;
///1:”27.55″,今日开盘价;
///2:”27.25″ ,昨日收盘价;
///3:”26.91″,当前价格;
///4:”27.55″,今日最高价;
///5:”26.20″ ,今日最低价;
///6:”26.91″,竞买价,即“买一”报价;
///7:”26.92″ ,竞卖价,即“卖一”报价;
///8:”22114263″ ,成交的股票数,由于股票交易以一百股为基本单位,所以在使用时,通常把该值除以一百;
///9:”589824680″ ,成交金额,单位为“元”,为了一目了然,通常以“万元”为成交金额的单位,所以通常把该值除以一万;
///10:”4695″,“买一”申请4695 股,即47手;
///11:”26.91″,“买一”报价;
///12:”57590″ ,“买二”
///13:”26.90″,“买二”
///14:”14700″ ,“买三”
///15:”26.89″ ,“买三”
///16:”14300″,“买四”
///17:”26.88″,“买四”
///18:”15100″ , “买五”
///19:”26.87″,“买五”
///20:”3100″,“卖一”申报3100股,即31手;
///21:”26.92″,“卖一”报价
///(22, 23), (24, 25), (26,27), (28, 29) 分别为“卖二”至“卖四的情况”
///30:”2008-01-11″ ,日期;
///31:”15:05:32″,时间;</param>
/// <returns></returns>
private Dictionary<string ,StockInfo> stockHtmlDeal(string stockHtml)
2、根据股票行情判断是否需要进行提示;
定义一个timer对象,一段时间对新浪股票行情接口访问一次,根据自己的需要编写股票行情提示条件判断方法【判断方法不在此赘述】,每个一段时间调用一次:
2 /// 定时器
3 /// </summary>
4 System.Timers.Timer Time = new System.Timers.Timer(30000);//1000为1秒
5
6 private void TimeSet()
7 {
8 Time.Elapsed += new ElapsedEventHandler(ListenerToStock);
9 Time.AutoReset = true;//设置是执行一次(false)还是一直执行(true);
10 Time.Enabled = true;//是否执行System.Timers.Timer.Elapsed事件;
11 }
12
13 private void ListenerToStock(object sender, ElapsedEventArgs e)
14 {
15 //todo:读股票行情,根据一定算法,判断是否发送提示信息。
16 }
3、发送短信提示;
这个部分花费了我大量时间。。并且目前还没达到自己满意的程度,如有可能,还会在这个环节的处理继续加强。
不过就这部分的开发来说,对我来说是挺有趣的。
A、现实如此残酷。。。
开发之前的想法,是以前经常看到一些Fetion的接口,觉得发短信很简单的,找个来调用就可以了,真到了要用的时候,发现网络上基本没有能发送成功的接口。。小弟不才,如果哪位大哥知道,请指点一下,不胜感激~~~~
B、免费的午餐。。。不怎么可口
颇费了一番周折之后,基本放弃了调用Fetion接口发短信的想法,折腾中看到免费的午餐 ——编程利用Google日历API发短信、Email,心花怒放,赶紧下了Google Data API,摸索一下Google Calendar的使用,用上了,收到短信提示的时候那叫一个开心~~
小小Reminder终于快要见到曙光了 ~~~~
然而,没开心多久,免费的午餐还是不那么好吃的。。。Google Calendar的SMS提示功能用起来很不稳定(可能也是我插入数据频繁),经常延迟很久甚至收不到消息,并且短信提示的内容也不够多(应该说是比较短) ,做到后期,基本感觉到如果用谷歌的提示,Reminder是没什么实用性了。。
C、终于吃到了可口的免费的午餐
最后我又想到了Fetion 。。。折腾阿。。想分析一些在用的飞信第三方软件,还特地跑到一个同学那里去请教请教,他做通信方面的,这方面比我强是肯定。。看到他用的139.com邮箱,里面有飞信功能,回家后自己也注册一个,居然看到还有新邮件免费短信提醒功能!!
上图。。。
我笑了。。。。真是踏破铁鞋无觅处,得来全不费工夫阿。。。。没那么时间去搞协议分析,真要搞怕要搞到明年了。。。我还是捡现成的吧。。。设置了短信提醒功能后,写个发邮件的方法,用发邮件触发移动的短信提示,而且提示的内容还相当全。。。搞定~出于偷懒的原因。。代码中直接写入了自己的邮箱信息,当然密码是加密了的,配置文件中配置toMailAddress,如果多个用户的话,相当于用我的邮箱往多个指定邮箱发邮件而已。
2 using System.Collections.Generic;
3 using System.Text;
4 using System.Net.Mail;
5 using System.Net;
6 namespace ReminderService
7 {
8 public class ReminderExtend
9 {
10 private static string from = "××××××@139.com";//发件人邮件地址
11 private static string userName = "××××××@139.com";//登录smtp主机时用到的用户名,注意是邮件地址'@'以前的部分
12 private static string password = "××××××";//登录smtp主机时用到的用户密码
13 private static string smtpHost = "smtp.139.com";//发送邮件用到的smtp主机
14
15 /// <summary>
16 /// 发送邮件
17 /// </summary>
18 /// <param name="to">收件人邮件地址</param>
19 /// <param name="from">发件人邮件地址</param>
20 /// <param name="subject">邮件主题</param>
21 /// <param name="body">邮件内容</param>
22 /// <param name="username">登录smtp主机时用到的用户名,注意是邮件地址'@'以前的部分</param>
23 /// <param name="password">登录smtp主机时用到的用户密码</param>
24 /// <param name="smtpHost">发送邮件用到的smtp主机</param>
25 public static void Send(string to, string subject, string body)
26 {
27 try
28 {
29 MailAddress _from = new MailAddress(from);
30 MailAddress _to = new MailAddress(to);
31 MailMessage message = new MailMessage(_from, _to);
32 message.Subject = subject;//设置邮件主题
33 message.IsBodyHtml = true;//设置邮件正文为html格式
34 message.Body = body;//设置邮件内容
35 SmtpClient client = new SmtpClient(smtpHost);
36 //设置发送邮件身份验证方式
37 //注意如果发件人地址是abc@def.com,则用户名是abc而不是abc@def.com
38 client.Credentials = new NetworkCredential(userName, password);
39 client.Send(message);
40 }
41 catch
42 { 要写日志还是干什么自己决定吧。。。}
43 }
44 public static void SendList(List<string> toMailList, string subject, string body)
45 {
46 foreach (string to in toMailList)
47 {
48 Send(to, subject, body);
49 }
50 }
51 }
52 }
用移动的邮箱发短信,爽就一个字。。。短信世家应该也不缺这点短信吧,嘎嘎。。。邮箱刚发出去,短信就到了~~~我真有种写一篇《免费的午餐 续》来歌颂下移动的冲动。。。太贴心了~~
为了软件的使用方便【配置股票代码,配置邮箱等】以及“安全”,还有一些附属功能:
【附:由于个人原因,我正常上班时间没有电脑可用。。。只好让小小Reminder寄生在女朋友上班用的电脑上。。太邪恶了。。】
1、配置使用者信息;
2、配置股票代码信息;
3、设置开机启动;
4、启动时即最小化到托盘;
1、读,写XML配置文件,惭愧的发现以前还真没怎么深入过,亲手做了一下,算是对XPath有了一定认识了。
2、设置开机启动,就是写注册表,没有做成启动时就设置,而是在winform界面做了个 设置/取消 开机启动的按钮。。。咱不是流氓软件哈
3、启动时即最小化到托盘花了一些时间,也和工作中主要是进行ASP.NET的开发,Winform做的比较少有关系吧。
最后实现的方法感觉还行,定义了一个类继承ApplicationContext,然后调用Application.Run(ApplicationContext context)方法,跳过了Application.Run(Form mainForm)强制显示form的处理。【简单的在程序中设置form的visible属性解决不了这个问题,因为最后还是会被强制显示】
贴代码了。。
2 using System.Collections.Generic;
3
4 using System.Windows.Forms;
5
6 namespace ReminderService
7 {
8 static class Program
9 {
10 /// <summary>
11 /// 应用程序的主入口点。
12 /// </summary>
13 [STAThread]
14 static void Main()
15 {
16 Application.EnableVisualStyles();
17 Application.SetCompatibleTextRenderingDefault(false);
18 //Application.Run(new ReminderService());
19
20 HideOnStartupApplicationContext context = new HideOnStartupApplicationContext(new ReminderService());
21 Application.Run(context);
22
23 }
24 }
25
26 /// <summary>
27 /// 用于实现持续启动时候不显示,本来用Application.Run(new ReminderService()),会强制显示
28 /// </summary>
29 internal class HideOnStartupApplicationContext : ApplicationContext
30 {
31 private Form mainFormInternal;
32
33 // 构造函数,主窗体被存储在mainFormInternal
34 public HideOnStartupApplicationContext(Form mainForm)
35 {
36 this.mainFormInternal = mainForm;
37
38 this.mainFormInternal.Closed += new EventHandler(mainFormInternal_Closed);
39 }
40
41 // 当主窗体被关闭时,退出应用程序
42 void mainFormInternal_Closed(object sender, EventArgs e)
43 {
44 Application.Exit();
45 }
46 }
47 }