【IT168 专稿】如今的桌面程序再不像过去那样完全独立于网络运行了,各式各样的功能都是离不开Internet连接的。程序往往需要Internet连接来进行诸如自动更新、WebService调用等类似存取远程数据的操作,但是在实际存取远程数据之前我们最好能够确保远程连接是可用的,这样程序的流程和健壮性就可圈可点了。通过编程手段动态检测Internet连接非常耗时,所以若不使用多线程,那么在检测时程序界面将会死锁,主线程的一切操作都被侦测连接所占去,在检测远程连接是否可用时主界面经常死锁可不是什么好的用户体验。本篇文章我们将针对该问题创建一个C#组件,主要目标就是创建一个标准框架用于完成远程连接的测试工作,而这种测试必须是多线程的、后台执行的,当连接检测成功后必须通知主线程对连接可用的情形作出反应,譬如打开一个网页、启动自动更新以及开始调用WebService等等。

在开始之前我们需要对该项目需要用到的相关技术做一下 List。
1.我们的组件应该继承自Component,因为它不需要控件那样的外观和可视化的操作,我们仅需要后台探测Internet连接,并将探测结果以事件的形式传递到主线程。并且这个组件应该在任何.Net项目中可重用。
2.我们不仅要探测Internet连接是否可用,还要检测指定的远程连接是否可用,譬如检测特定的WebService是否可用等等。这就要求我们不能简单使用Win32API或者WMI来检测网卡或者ADSL的运行状态来表示Internet连接。
3.必须使用多线程后台检测连接,推荐使用BackgroundWorker。
4.检测完毕必须通知主线程进行相应操作,线程间操作非事件莫属。
5.主界面应该对不同的连接状态做出可视化的响应,这也必须借助事件来完成。
在检测Internet连接的方法上,我们采用了另辟蹊径的策略。可以想象,任何一个客户程序需要连接Internet时肯定会有一个地址,这个地址无非是包含了一个ip地址或域名以及端口数据罢了,通过这个地址客户程序和远程主机建立连接来进行数据交换。那我们的客户程序就直接检测这个地址可用与否来近似判断Internet连接是否可用,地址可用证明Internet连接也可用,地址不可用证明两点:1、地址无效;2、Internet连接不可用。但是地址无效的可能性很小,首先程序员不可能提供一个无效地址让客户程序去尝试连接,再有服务器端瘫痪的可能性也不大,譬如尝试连接www.microsoft.com 或者 www.google.cn。
好了,我们开始创建我们的检测组件。
首先我们创建一个Windows Form Application,然后添加一个组件,这些过程不再详述。我们将新建的组件命名为InternetConnection,我们先定义两个私有字段BackgroundWorker类型的bgworker和string类型的reliableURL。然后定义两个属性:
public
string
ReliableURL
{
get
{
return
reliableURL; }
set
{ reliableURL
=
value; }
}
public
bool
Active
{
set
{
if
(value
==
true
)
{
bgworker.RunWorkerAsync();
}
else
{
bgworker.CancelAsync();
}
}
}
ReliableURL属性就是我们前述的所谓远程地址,我们通过访问一个可靠地址或者我们自己的WebService地址来间接检测Internet连接是否可用。Active属性允许控制BackgroundWorker的异步执行状态,通过设置该组件Active属性为true来启动后台Internet连接检测。
接下来就需要定义事件了。简单起见我们为这个组件定义了两个事件,Connected和ConnectFailure,前者是连接成功后激发的事件,后者是连接失败激发的事件。我们事件不需要参数传递,更多关于事件的叙述请参考我的另一篇博文:.Net事件与委托
public
event
EventHandler Connected;
public
event
EventHandler ConnectFailure;
protected
virtual
void
OnConnected(EventArgs e)
{
if
(Connected
!=
null
)
{
Connected(
this
, e);
}
}
protected
virtual
void
OnConnectFailure(EventArgs e)
{
if
(ConnectFailure
!=
null
)
{
ConnectFailure(
this
, e);
}
}
然后,我们创建检测连接的代码,关于更多使用BackgroundWorker的方法请参考MSDN或者我的另一篇博文:实战.Net多线程(三) 和 实战.Net多线程(四)
private
void
BackgroundWorker_DoWork(
object
sender, DoWorkEventArgs e)
{
try
{
HttpWebRequest request
=
(HttpWebRequest)HttpWebRequest.Create(reliableURL);
HttpWebResponse response
=
(HttpWebResponse)request.GetResponse();
if
(HttpStatusCode.OK
==
response.StatusCode)
{
response.Close();
e.Result
=
true
;
}
else
{
e.Result
=
false
;
}
}
catch
(WebException)
{
e.Result
=
false
;
}
}
private
void
BackgroundWorker_RunWorkerCompleted(
object
sender, RunWorkerCompletedEventArgs e)
{
if
(e.Error
!=
null
)
{
throw
e.Error;
}
else
{
if
((
bool
)e.Result)
//
Online
{
OnConnected(e);
}
else
//
Offline
{
OnConnectFailure(e);
}
}
}
最后,在组件的初始化方法中实例化相关私有字段。
public
InternetConnection(IContainer container)
{
container.Add(
this
);
InitializeComponent();
bgworker
=
new
BackgroundWorker();
bgworker.WorkerReportsProgress
=
false
;
bgworker.WorkerSupportsCancellation
=
false
;
bgworker.DoWork
+=
new
DoWorkEventHandler(
this
.BackgroundWorker_DoWork);
bgworker.RunWorkerCompleted
+=
new
RunWorkerCompletedEventHandler(
this
.BackgroundWorker_RunWorkerCompleted);
}
组件创建完,全部编译下整个解决方案,打开最初创建的项目WindowsFormsApplication1的窗体Form1,我们会在VS IDE中的工具箱中发现我们刚刚创建的名为InternetConnection组件,将其拖到Form1窗体上,便有了其类型的实例internetConnection1。在窗体上我们放置一个Button、一个WebBrowser和一个StatusStrip,并且在StatusStrip中添加一个StatusLabel。我们利用WebBrowser显示一个特定网页,来表示Internet连接可用。
选中 internetConnection1 我们会发现其公开了两个事件,我们为这两事件添加相应处理代码:
private
void
internetConnection1_Connected(
object
sender, EventArgs e)
{
toolStripStatusLabel1.Image
=
Properties.Resources.Online;
toolStripStatusLabel1.Text
=
"
已连接到 Internet,正在打开 it168.com 网页....
"
;
webBrowser1.Navigate(
"
http://www.it168.com/
"
);
}
private
void
internetConnection1_ConnectFailure(
object
sender, EventArgs e)
{
toolStripStatusLabel1.Image
=
Properties.Resources.Offline;
toolStripStatusLabel1.Text
=
"
未连接到 Internet
"
;
webBrowser1.Navigate(Application.StartupPath
+
"
\\Error.html
"
);
}
连接成功后将显示一个远程网页,失败时将显示一个自定义Html文件。如下图:


对WebBrowser的DocumentCompleted事件中添加相应代码以对网页装载完成做出响应:
private
void
webBrowser1_DocumentCompleted(
object
sender, WebBrowserDocumentCompletedEventArgs e)
{
toolStripStatusLabel1.Text
=
"
完成
"
;
}
最后在Button的单击事件中添加开始测试连接代码:
private
void
button1_Click(
object
sender, EventArgs e)
{
internetConnection1.ReliableURL
=
"
http://www.google.cn
"
;
//
要检测的网络连接
internetConnection1.Active
=
true
;
toolStripStatusLabel1.Image
=
Properties.Resources.Searching;
toolStripStatusLabel1.Text
=
"
正在尝试连接远程服务器....
"
;
}
至此,一个自定义Internet连接组件就创建完毕。该程序在Windows SP3 + Visual Studio 2008 SP1 环境下编译调试成功。