1、用一文本编辑框用于向用户显示输出结果。
2、增加另一个编辑框,ID标识符为IDC_EDIT2,选中Mutiline和Want return属性。这个编辑框用于接受用户输入网址。
3、在对话框上布置5个按钮,分别用来实现HTTP、FTP、GOPHER、FINGER和WHOIS查询。ID标识符分别为 IDC_BUTTON_HTTP、IDC_BUTTON_FTP、IDC_BUTTON_GOPHER、IDC_BUTTON_FINGER、 IDC_BUTTON_WHOIS。
下面,我们开始给控件附加变量和代码,以完成程序功能。首先,我们来看如何使用HTTP协议进行查询。
在编写实际进行查询的代码前,应给两个编辑框附加变量,以实现输入和输出的目的。我们使用classwizard,给编辑框IDC_EDIT1附加变量m_out,给编辑框IDC_EDIT2附加变量m_host。它们都应为Cstring类型的变量。
下面,我们给对话框类CQueryDlg添加一个函数,它用于实现HTTP查询。函数原形为:
void TryURL(Cstring URL);
其实现代码为:
void CQueryDlg::TryURL(Cstring URL)
{
CInternetSession session;
m_out+="正在链接"+URL+"\r\n";
UpdateData(FALSE);
CInternetFile* file=NULL;
try
{
file=(CInternetFile*)session.OpenURL(URL);
}
catch(CInternetException* pEx)
{
file=NULL;
pEx->Delete();
}
if(file)
{
m_out+="已建立链接。\r\n";
Cstring line;
for(int I=0;I<20&&file->ReadString(line);I++)
{
m_out+=line+"\r\n";
}
file->Close();
delete file;
}
else
{
m_out+="本地址没有发现http主机\r\n";
}
m_out+="------------------------------------------------------\r\n";
UpdateData(FALSE);
}
这个函数是如何实现HTTP链接的呢,我们分析如下:
首先,建立一个internet会话,这要定义一个CInternetSession的对象。其构造函数的原形如下:
CInternetSession( LPCTSTR pstrAgent = NULL, DWORD dwContext = 1, DWORD dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG, LPCTSTR pstrProxyName = NULL, LPCTSTR pstrProxyBypass = NULL, DWORD dwFlags = 0 );
这个函数需要定义很多参数,但是,本程序都使用缺省值,即“=”号后的值。CInternetSession构造函数参数说明如下:
LPCTSTR pstrAgent-应用程序名,如果为NULL,它将替你填入你在AppWizard中给定的程序名。
DWORD dwContext-本操作的设备关联符定义。
DWORD dwAccessType-访问类型,为以下参数之一,INTERNET_OPEN_TYPE_PRECONFIG (default), INTERNET_OPEN_TYPE_DIRECT, 或 INTERNET_OPEN_TYPE_PROXY
LPCTSTR pstrProxyName-如访问类型为INTERNET_OPEN_TYPE_PROXY,则给该参数赋予协议名称。
LPCTSTR pstrProxyBypass-如访问类型为INTERNET_OPEN_TYPE_PROXY,则该参数为不通过协议服务器而直接链接的一系列地址。
DWORD dwFlags-可为以下参数,INTERNET_FLAG_DON'T_CACHE, INTERNET_FLAG_ASYNC, 和INTERNET_FLAG_OFFLINE.
dwAccessType值缺省时将使用系统注册簿定义的值。显然,程序允许使用者定义访问类型将比由程序内部直接定义要好。因此,要正确使用本程序,必需先在windows系统中定义好网络访问类型,步骤如下:
1、双击桌面上“my computer"图标。
2、点击“contyol panel".
3、点击“internet ”。
4、在随后弹出的对话框中,选“connection"栏,然后填写网络连接属性。如果你是拨号上网,选中“dial”选择项,并填写相关属性。如果你是通过proxy服务器上网,选中“proxy”选项,点击“setting”按钮,设置proxy服务器地址和端口号。如果你是直接连入 internet,应使所有的选项均为非选中状态。
本程序在构造CInternetSession对象时使用缺省值,因此,构造函数将不带任何参数。如下所示:
CInternetSession session;
在构造对象session后,我们需写两行程序作一些输出,表示程序已开始工作。
m_out+="正在链接"+URL+"\r\n";
UpdateData(FALSE);
接下来我们使用session对象的成员函数OpenURL()来打开一个URL资源。该函数返回一个文件的指针,文件类型为以下四种之一:
file:// 如果访问的是本地机器,函数返回一个CStudioFile类对象的指针。
ftp:// 如果访问的是一ftp地址,函数返回一个CInternetFile类对象的指针。
gopher:// 如果访问的是一gopher地址,函数返回一个CGopherFile类对象的指针。
http:// 如果访问的是一http地址,函数返回一个CHttpFile类对象的指针。
本程序用于访问远程机器,因此,函数不会返回一个file://类型的本地文件。而CGopherFile和CHttpFile均派生自 CInternetFile,所以将该函数返回值赋给CInternetFile类的指针是安全的。当OpenURL()不能正常打开URL资源时,该函数将会抛出一个异常(exception),从而导致程序运行时错误。由于本程序用于探查未知的网址,该网址可能不提供相应服务或根本不存在,因此 OpenURL()有时可能不会正常运行。为了避免程序异常终止,我们需使用try-catch结构来处理异常。本段程序代码如下:
CInternetFile* file=NULL;
try
{
file=(CInternetFile*)session.OpenURL(URL);
}
catch(CInternetException* pEx)
{
file=NULL;//如果发生运行时错误,给file赋空值,程序将继续运行
pEx->Delete();
}
通过以上代码,程序将使用用户指定的地址来试图打开Http网址,如果失败,返回的文件为空,程序将继续运行,尝试用其它协议打开网址。随后,无论网址是否成功打开,我们都应作相应输出以提示用户。如果成功,我们用一个for循环读出返回文件的头20句并输出,如果失败,也要给出相应的提示。程序如下:
if(file) //判定链接是否成功
{
m_out+="已建立链接。\r\n";
Cstring line;
for(int I=0;I<20&&file->ReadString(line);I++)
{
m_out+=line+"\r\n";
}
file->Close();
delete file;
}
else
{
m_out+="本地址没有发现http主机\r\n";
}
m_out+="------------------------------------------------------\r\n";
UpdateData(FALSE);
用于查询的函数编写完后,我们还要给按钮的单击事件增加代码,以便按钮按下时,程序开始查询。给按钮IDC_BUTTON_HTTP按钮增加BN_CLICKEDvoid 消息的代码如下:CQueryDlg::OnQueryHttp()
{
const Cstring http = "http://";
UpdateData(TRUE);
m_out = "";
UpdateData(FALSE);
TryURL(http + m_host);
TryURL(http + "www." + m_host);
}
这段代码中,UpdateData(TRUE)的调用将给m_host赋予用户定义的值。UpdateData(FALSE)语句将清空输出编辑框变量 m_out的内容。接下来调用两次TryURL(),例如说,当用户输入yahoo.com,程序将首先试一试http://yahoo.com,然后试一试http://www.yahoo.com。好了,我们现在可以编译程序并执行,输入yahoo.com,程序将连接到internet,并输出 yahoo主页的头20行。
实现FTP链接
下面,我们来探讨如何实现FTP链接。首先给对话框类增加一个实现链接的函数TryFTP:
void CQueryDlg::TryFTP(Cstring host)
{
CInternetSession session;
m_out += "正在链接FTP地址 " + host + "\r\n";
UpdateData(FALSE);
CFtpConnection* connection = NULL;
try
{
connection = session.GetFtpConnection(host);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "已建立链接。 \r\n";
Cstring line;
connection->GetCurrentDirectory(line);
m_out += "缺省目录为 " + line + "\r\n";
connection->Close();
delete connection;
}
else
{
m_out += "本地址没有发现ftp主机 。\r\n";
}
m_out += "------------------------------------------------------\r\n";
UpdateData(FALSE);
}
本函数和TryURL()很相似,不过,它不使用OpenURL()来打开一个文件,而是用GetFtpConnection()来与ftp服务器建立链接。如果链接成功,将使用GetCurrentDirectory()来得到服务器的缺省目录。
大多数ftp地址前有ftp.的前缀,但一些老的地址没有此前缀。现在,给IDC_BUTTON_FTP链接单击消息的处理函数,并编写如下:
void CQueryDlg::OnButtonFtp()
{
UpdateData(TRUE);
m_out = "";
UpdateData(FALSE);
TryFTP(m_host);
TryFTP("ftp." + m_host);
}
现在我们已完成了FPT连接的实现代码。重新编译程序并运行,键入ftp.microsoft.com。可以发现,在ftp.microsoft.com 处提供ftp服务。但是你必须有一点耐性,因为在开始查询到出结果将有较长一段时间,因为程序等结果全部探查到后再一次显示,但实际上现在的商业 internet程序都是实时显示的。如果我们希望结果实时地显示,必需使用异步套接字(asynchronous sockets)或多线程编程。
实现Gopher查询
GOPHER是一种基于文本的协议,它和WWW相似,可以通过点击文字内容,实现网络内的链接和浏览。但是,GOPHER是通过逐级文字菜单来组织链接和内容的,它不象WWW那样有丰富的多媒体页面。要实现GOPHER查询,我们应给CQueryDlg类增加另一个成员函数void TryGopher (Cstring host)。如果查询成功,该函数将返回查询地址的第一个Gopher位置(locator)。位置是gopher协议的概念,任何 gopher客户程序必需先得到一个gopher位置,然后才能进行相应的gopher操作。TryGopher()函数如下:
void CQueryDlg::TryGopher(Cstring host)
{
CInternetSession session;
m_out += "正在链接gopher地址 " + host + "\r\n";
UpdateData(FALSE);
CGopherConnection* connection = NULL;
try
{
connection = session.GetGopherConnection(host);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "已建立链接。 \r\n";
Cstring line;
CGopherLocator locator = connection->CreateLocator(NULL, NULL, GOPHER_TYPE_DIRECTORY);
line = locator;
m_out += "第一个Gopher位置是" + line + "\r\n";
connection->Close();
delete connection;
}
else
{
m_out += "本地址没有发现gopher主机 。 \r\n";
}
m_out += "------------------------------------------------------\r\n";
UpdateData(FALSE);
}
本函数和前两个函数大致相似,在通过调用connection = session.GetGopher Connection(host);来建立 Gopher链接后,通过语句CGopherLocator locator = connection-> CreateLocator (NULL, NULL, GOPHER_TYPE_DIRECTORY);来建立一个Gopher位置(locator),函数 CreateLocator()有多个版本,现在我们使用的是其含三个参数的版本。其原形为CGopherLocator CreateLocator ( LPCTSTR pstrDisplayString, LPCTSTR pstrSelectorString, DWORD dwGopherType );。其中参数pstrDisplayString指明要查询的服务器上的具体文件或目录名,如果它为NULL,则返回服务器缺省目录名;参数 pstrSelectorString是发送给服务器的字符命令,以便检索某个项目,它可设为空;参数dwGopherType指明Gopher访问类型,本例中定义为GOPHER_TYPE_DIRECTORY,指明要访问的是目录。它的其它可取值请参考VC++5.0文档。Gopher位置 (locator)建立后,我们把它强制转换为Cstring类型,并把该位置显示出来。
现在,给IDC_BUTTON_GOPHER链接单击消息的处理函数,并编写如下:
void CQueryDlg::OnButtonGopher()
{
UpdateData(TRUE);
m_out = "";
UpdateData(FALSE);
TryGopher(m_host);
TryGopher("gopher." + m_host); // TODO: Add your control notification handler code here
}
重新编译程序,输入地址harvard.edu,程序将会探查出它是一个Gopher地址,并显示出第一个Gopher位置。
如何建立FINGER查询
Finger协议的作用是给你提供一个网址的具体情况,它是Internet上最古老的协议之一。在一个Finger服务器上,你可以查询它的某一个用户或整个网址的情况。当然,这对网络的安全是不利的,实际上,有经验的黑客们在攻击一个未知网络时,第一步就是向它发送Finger和Whois查询,这也是黑客网址上的黑客教程中建议的步骤。为了安全,许多网络服务器不提供Finger服务,然而,当它接受到Finger查询请求时,仍然会返回一些其它的有用信息。
在MFC和WIN32 API中,没有提供直接实现Finger查询的函数,但是,我们仍有变通的办法来实现它。所有的internet链接都需要一个宿主名和端口号,所有的著名的服务都有其特定的端口号,例如:http服务使用远程宿主机上的端口80,ftp服务使用端口21,gopher服务使用端口 70。对于finger服务来说,它使用端口79。finger是一种简单的协议,如果你向远程宿主机的端口79发送字符串,finger服务器在端口 79侦听到后,将会发送出一个finger回答。如果你发送的字符串仅仅包含\r\n,服务器通常将会把本服务器上所有用户的列表及相关信息(如用户真实姓名等)做为应答返回。因此,如果我们不使用缺省的端口70,而是使用端口79来建立gopher链接,我们就能发出finger查询。给 CQueryDlg类增加一个成员函数void TryFinger(Cstring host)如下:
void CQueryDlg::TryFinger(Cstring host)
{
CInternetSession session;
m_out += "正在链接finger地址 " + host + "\r\n";
UpdateData(FALSE);
CGopherConnection* connecti
on = NULL;
try
{
connection = session.GetGopherConnection(host,NULL,NULL,79);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "已建立链接。 \r\n";
CGopherLocator locator = connection->CreateLocator(NULL, NULL, GOPHER_TYPE_TEXT_FILE);
CGopherFile* file =NULL;
try
{
file = connection->OpenFile(locator);
}
catch (CInternetException* pEx)
{
file = NULL;
pEx->Delete();
}
if (file)
{
Cstring line;
for (int I=0; I < 20 && file->ReadString(line); I++)
{
m_out += line + "\r\n";
}
file->Close();
delete file;
}
else
{
m_out+="finger查询失败。\r\n";
}
connection->Close();
delete connection;
}
else
{
m_out += "本地址没有发现finger主机 。 \r\n";
}
m_out += "------------------------------------------------------\r\n";
UpdateData(FALSE);
}
本函数中,语句connection = session.GetGopherConnection(host,NULL,NULL,79);用于建立 finger链接。随后,我们创立一个文本文件类型的Gopher位置用来操作服务器返回的信息:CGopherLocator locator = connection->CreateLocator(NULL, NULL, GOPHER_TYPE_TEXT_FILE);。使用该 Gopher位置打开文件并使用一个for循环来读出该文件的头20行,随后将它显示出来。
现在,给IDC_BUTTON_FINGER按钮链接单击消息的处理函数,并编写如下:
void CQueryDlg::OnButtonFinger()
{
UpdateData(TRUE);
m_out = "";
UpdateData(FALSE);
TryFinger(m_host);
// TODO: Add your control notification handler code here
}
编译程序,输入地址whitehouse.gov,程序将会返回该服务器的e-mail地址,从返回的信息可知,出于安全考虑,该服务器的其它 finger服务已被取消了。如果你输入的网址没有提供finger服务,程序将有较长一段时间没有反应,最后弹出一个消息框,通知用户链接出现超时错误,单击ok按钮即可。
实现WHOIS查询
还有一个协议也能提供网址的相关信息,它也是一种古老的协议,MFC并不直接支持它,这就是whois协议。在整个internet上,只有少数服务器提供whois服务。whois服务建立了internet上的域名数据库,如果对某个域名进行whois查询,服务器将会返回拥有该域的机构或个人的实际姓名、地址、电话号码等信息。国际上的域名注册机构拥有whois服务器,例如,域名结尾为.com的域都在一个称为InterNIC的机构中注册,该机构拥有一个whois服务器称为rs.internic.net。whois协议和finger协议一样,是一种简单的协议,它使用端口43,如果向 whois服务器的端口43发送包含域名的字符串,则whois服务器将会返回该域拥有者的情况。给CQueryDlg类增加一个成员函数 void TryWhois(Cstring host)如下:
void CQueryDlg::TryWhois(Cstring host)
{
CInternetSession session;
m_out += "正在链接Whois地址 " + host + "\r\n";
UpdateData(FALSE);
CGopherConnection* connection = NULL;
try
{
connection = session.GetGopherConnection("rs.internic.net",NULL,NULL,43);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "已建立链接。 \r\n";
CGopherLocator locator = connection->CreateLocator(NULL, host, GOPHER_TYPE_TEXT_FILE);
CGopherFile* file = NULL;
try
{
file = connection->OpenFile(locator);
}
catch (CInternetException* pEx)
{
file = NULL;
pEx->Delete();
}
if (file)
{
Cstring line;
for (int I=0; I < 20 && file->ReadString(line); I++)
{
m_out += line + "\r\n";
}
file->Close();
delete file;
}
else
{
m_out+="Whois查询失败。\r\n";
}
connection->Close();
delete connection;
}
else
{
m_out += "Whois查询失败。\r\n";
}
m_out += "------------------------------------------------------\r\n";
UpdateData(FALSE);
}
在本函数中,语句connection = session.GetGopherConnection("rs.internic.net",NULL, NULL,43);使程序链接到了whois服务器rs.internic.net。随后,我们建立一个gopher位置来查询用户输入的域: CGopherLocator locator = connection->CreateLocator(NULL, host, GOPHER_TYPE_TEXT_FILE); 由于链接的域为rs.internic.net,所以,本函数只能查询结尾为.com的域。读者可以对本段程序进行扩充,链接到其它相关whois服务器,以查询其它类型的域。
现在,给IDC_BUTTON_WHOIS按钮链接单击消息的处理函数,并编写如下:
void CQueryDlg::OnButtonWhois()
{
UpdateData(TRUE);
m_out = "";
UpdateData(FALSE);
TryWhois(m_host);// TODO: Add your control notification handler code here
}
重新编译程序,现在,我们可以对结尾为.com的域进行whois查询了。
至此,本程序已全部结束。当然,有兴趣的读者可以对它进一步扩充,以完成更多的功能。可以对WinInet类进行一些小小的扩充,以便能通过特定的端口访问e-mail和news服务。此外,也可以链接到一些著名的网络搜索引擎上并提交查询,使程序具有搜索功能。这一切,就取决于你的创造力了。