Reminder是我最近开发的一个股票行情短信提示软件(刚做完,还是不够稳定。。)。开发这个小东东的想法缘起前段时间在首页看到一篇介绍开放Web Service的文章,其中有个Web Service就是获取实时股票行情的,看到后突然有了做个行情短信提示软件的想法,主要也是由于个人需求驱动,接着。。。革命开始了。。
Reminder的基础功能 很简单:
1、实时获取股票行情;
2、根据股票行情判断是否需要进行提示;
3、发送短信提示;
可是做起来也挺花时间。。。。接下来逐一介绍吧,这里主要提供思路,代码比较简单,也完成的比较仓促,就不全发了。
1、获取股票行情
虽然做Reminder的想法缘起Web Service,不过最后实现的时候是调用了新浪的股票行情接口, 之所以选用后者处于两方面的原因:第一,出于稳定性的考虑,觉得新浪比较靠谱。。第二,个人感觉使用更方便一点,并且一次访问可以提取多个股票行情,配置多只股票的话,访问次数少。代码很简单:
1
///
<summary>
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之后当然就是进行处理,这里只贴出返回数据的格式,具体应用中要如何定义数据结构请自便。。
返回的HTML
///
<summary>
///
解析从 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对象,一段时间对新浪股票行情接口访问一次,根据自己的需要编写股票行情提示条件判断方法【判断方法不在此赘述】,每个一段时间调用一次:
1
///
<summary>
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,如果多个用户的话,相当于用我的邮箱往多个指定邮箱发邮件而已。
发送邮件
1
using
System;
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
//
注意如果发件人地址是[email protected] ,则用户名是abc而不是[email protected]
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属性解决不了这个问题,因为最后还是会被强制显示 】
贴代码了。。
代码
1
using
System;
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
}
程序部署碎碎念 最后,把程序打包,发给My Girl,教她安好,设置开机启动~~~今天收到了30来条短信,还算不错,不过中间有部分短信延迟。。。看来Reminder还是需要进一步加强哈~~
期间有过一次更新程序,以为得重新做过一个安装包了,后来确定只需要替换两个文件。。。突然又有了想法, 某些软件的更新 应该就是这么做的吧:网络上部署一个存放最新程序的空间,软件启动伴随启动一个update进程,比对当前软件版本与网络空间上最新版本的 版本号,如不一致,则通过这个update进程控制,下载,关闭软件程序,替换文件,重启软件程序。。。。。
接触winform比较少,说来惭愧,之前还没完整的做过打包,部署等工作,这次算是亲身经历了一回。。My Girl环境还没有.Net Framework。。。略过无数波折。。。。亲身体会到软件部署的难易程度 对于软 件生命力的影响何其巨大,要不是我要用,估计她【电脑入门水平】无论如何也不会装个还需要装.NET环境的软件,甚至还装不大来呢。。。抛开平台不说,开 发任何软件,使用(特别是上手)的 简易性 永远都是值得追求的。