使用MFC編寫internet查詢程式 (轉)
使用MFC編寫inte查詢
杜經農
在VC++ 5.0中,MFC的WinInet類包裝了相關的用於internet 客戶機程式的 。這樣,無需瞭解winsock或TCPIP的細節就可以編制出internet客戶機程式。本文中,我們將探討如何使用WinInet類來編寫一個internet查詢程式,該程式起名為“探路者”。該程式能使用各種來查詢,包括古老的FINGER和WHOIS。
顧名思義,“探路者”用於探查一部internet的情況,檢視它能提供哪些服務。例如說,你知道某人的e-地址,假設是wang@eop.com,我們就可以透過該程式,嘗試用各種辦法建立到eop.com的連結,隨後對該指定域依次採用WWW查詢、查詢、GOPHER查詢、FINGER查詢和WHOIS查詢。根據返回的結果,可以判斷該域能提供哪些服務。此外,如果你想給自己的伺服器起某個域名,你也可以透過本程式知道該域名是否已被採用。
一、查詢程式介面的實現
本程式的介面將基於對話方塊來實現。建立介面的步驟如下:
在developer studio中,
1、選擇file-new…,高亮MFC appwizard(exe),在Project name欄內給程式取名為query ,單擊ok。
2、在step-1中,單擊dialog based,選English做為資源語言。單擊next。
3、在step-2中,取消 Controls的選中標記。不要選擇 socket(該程式不直接socket函式),在對話方塊標題欄中鍵入“探路者”。
4、在step-3中,選擇yes,please和As a ststically linked library
5、在step-4中,不要改變appwiard所生成的任何類名,appwizard結束後,我們準備著手編寫程式的核心部分。
appwizard建立了一個空的對話方塊,我們將從它的基礎上開始工作。下面編輯對話方塊:
選re view ,展開query resources-dialogs,雙擊IDD-QUERY-DIALOG 資源,下面編輯對話方塊:
1、將OK按鈕改為“查詢主機”,右擊該按鈕,選擇properties,將該按鈕的ID名改為id-query。
2、將CANCEL按鈕改為“結束查詢”。
3、刪除to do等靜態文字。
4、將對話方塊增大到300畫素寬
5、在對話方塊頂端增加一個編輯框,資源id名為IDC-HOST,將編輯框拉伸到最寬。
6、給編輯框增加一個名為“地址名”的標籤。
7、將對話方塊拉伸到150畫素高。
8、在對話方塊底部增加另一個編輯框,id名為IDC-OUT,將它拉伸到儘可能的大,填滿對話方塊的剩餘空間。
9、給該編輯框增加Multi-line,Horizontal scroll,Vertical scroll,Border,Read-only等特性。
當使用者單擊“查詢主機”按鈕時,程式將使用各種方法查詢網址。因此,需使用classwizard,把實現查詢功能的程式碼連結到query按鈕。
1、選view-classwizard。
2、選CQueryDlg類,主機名將由該類接受。
3、高亮ID-query,高亮右邊列表欄裡的BN-CLICKED。
4、按Add Fuction按鈕,增加一個函式。
5、Class Wizard建議的名字為OnOk,將它改為OnQuery ,,單擊ok
6、單擊成員變數欄,準備將edit控制連線到對話方塊類的變數
7、高亮IDC-HOST並單擊Add Variable。將把EDIT控制連線到對話方塊類的Cstring成員變數,名為m_host
8、類似以上步驟,將IDC-OUT連結到CString變數m_out
點選ok關閉classwizard,剩下的工作是編寫CQueryDlg::OnQuery()函式,它將使用m_host值進行查詢並將結果輸出到m_out變數。
二、編寫程式碼,進行http查詢
查詢一個internet地址時,首先應嘗試建立http連結。這是因為大量的地址都含有頁面。使用http建立連結的最簡單的辦法是使用winlnet類CInternetSession,並呼叫其成員函式OpenURL()。該函式將返回一個,我們在m_out中顯示該檔案的頭幾行文字。首先,在QueryDLG.CPP的開始加入以下行:
#include‘afxinet.h’
這將定義訪問Winlnet類的程式碼。由於本程式將訪問大量的URLS,在CQueryDlg中增加一個名為TryURL的函式,它含有一個CString類的引數,名為URL,返回值為void。右擊CQueryDLG類,選擇Add Member function…。增加函式TryURL(),將它定義為protected型別。這個新函式將從CQueryDlg::OnQuery()中呼叫。在OnQuery()中增加以下程式碼:
void CQueryDlg::OnQuery()
{ 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(),例如說,當使用者輸入.com,程式將首先試一試然後試一試。以下是TryURL的程式碼。
void CQueryDlg::TryURL(CString URL)
{
CInternetSession session;
m_out+="正在連結"+URL+"rn";
UpdateData(FALSE);
CInternetFile* file=NULL;
try
{
file=(CInternetFile*)session.OpenURL(URL);
}
catch(CInternetException* pEx)
{
file=NULL;
pEx->Delete();
}
if(file)
{
m_out+="已建立連結。rn";
CString line;
for(int i=0;i<20&&file->ReadString(line);i++)
{
m_out+=line+"rn";
}
file->Close();
delete file;
}
else
{
m_out+="本地址沒有發現http主機rn";
}
m_out+="------------------------------------------------------rn";
UpdateData(FALSE);
}
下面,我們對以上程式碼進行逐段分析。首先,建立一個internet區,這要定義一個CInternetSession的。其建構函式的原形如下:
CInternetSession( LPCTSTR pstrAgent = NULL, D dwContext = 1, DWORD dwAccessType = INTERNET_OPEN_TYPE_PRECONFIG, LPCTSTR pstrName = 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_DONT_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+"rn";
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+="已建立連結。rn";
CString line;
for(int i=0;i<20&&file->ReadString(line);i++)
{
m_out+=line+"rn";
}
file->Close();
delete file;
}
else
{
m_out+="本地址沒有發現http主機rn";
}
m_out+="------------------------------------------------------rn";
UpdateData(FALSE);
現在我們可以編譯該程式並執行,鍵入地址microsof.com並查詢,輸出的資訊表明,程式在和處均發現了www網頁,並輸出了主頁的HTML檔案的頭20行。
三、ftp查詢的實現
現在我們繼續程式設計,來探查使用者輸入的網址是否提供ftp服務。ftp是internet上的一種協議,主要用於在伺服器和客戶機之間傳送檔案。重新呼叫TryURL()來達到目的是不可能的,因為TryURL()假設該網址指向一個檔案,而一個ftp網址指向一系列檔案。必需重新編寫一個函式,仿照以前步驟,給CQueryDlg類增加另一個成員函式void TryFtp(CString host)。如果所探查的網址提供ftp服務,該函式將找出ftp預設目錄並顯示出來。程式碼如下:
void CQueryDlg::TryFTP(CString host)
{
CInternetSession session;
m_out += "正在連結FTP地址 " + host + "rn";
UpdateData(FALSE);
CFtpConnection* connection = NULL;
try
{
connection = session.GetFtpConnection(host);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "已建立連結。 rn";
CString line;
connection->GetCurrentDirectory(line);
m_out += "預設目錄為 " + line + "rn";
connection->Close();
delete connection;
}
else
{
m_out += "本地址沒有發現ftp主機 。rn";
}
m_out += "------------------------------------------------------rn";
UpdateData(FALSE);
}
本函式和TryURL()很相似,不過,它不使用OpenURL()來開啟一個檔案,而是用GetFtpConnection()來與ftp伺服器建立連結。如果連結成功,將使用GetCurrentDirectory()來得到伺服器的預設目錄。
大多數ftp地址前有ftp.的字首,但一些老的地址沒有此字首。我們可以在OnQuery()函式的末尾增加兩行來呼叫這個新的函式:
TryFTP(m_host);
TryFTP("ftp."+m_host);
重新編譯程式並執行,可以發現,在ftp.microsoft.com處提供ftp服務。在開始查詢到出結果將有一些延遲,因為程式等結果全部探查到後再一次顯示,如果我們希望結果實時地顯示,必需使用非同步套接字(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 + "rn";
UpdateData(FALSE);
CGopherConnection* connection = NULL;
try
{
connection = session.GetGopherConnection(host);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "已建立連結。 rn";
CString line;
CGopherLocator locator = connection->CreateLocator(NULL, NULL, GOPHER_TYPE_DIRECTORY);
line = locator;
m_out += "第一個Gopher位置是" + line + "rn";
connection->Close();
delete connection;
}
else
{
m_out += "本地址沒有發現gopher主機 。 rn";
}
m_out += "------------------------------------------------------rn";
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 pstrorString, DWORD dwGopherType );。其中引數pstrDisplayString指明要查詢的伺服器上的具體檔案或目錄名,如果它為NULL,則返回伺服器預設目錄名;引數pstrSelectorString是傳送給伺服器的字元命令,以便檢索某個專案,它可設為空;引數dwGopherType指明Gopher訪問型別,本例中定義為GOPHER_TYPE_DIRECTORY,指明要訪問的是目錄。它的其它可取值請參考VC++5.0文件。Gopher位置(locator)建立後,我們把它強制轉換為Cstring型別,並把該位置顯示出來。在OnQuery()函式的末尾加入以下行:
TryGopher(m_host);
TryGopher("gopher." + m_host);
重新編譯程式,輸入地址harvard.edu,程式將會探查出它是一個Gopher地址,並顯示出第一個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回答。如果你傳送的字串僅僅包含rn,伺服器通常將會把本伺服器上所有使用者的列表及相關資訊(如使用者真實姓名等)做為應答返回。因此,如果我們不使用預設的埠70,而是使用埠79來建立gopher連結,我們就能發出finger查詢。給CQueryDlg類增加一個成員函式void TryFinger(CString host)如下:
void CQueryDlg::TryFinger(CString host)
{
CInternetSession session;
m_out += "正在連結finger地址 " + host + "rn";
UpdateData(FALSE);
CGopherConnection* connection = NULL;
try
{
connection = session.GetGopherConnection(host,NULL,NULL,79);
}
catch (CInternetException* pEx)
{
connection = NULL;
pEx->Delete();
}
if (connection)
{
m_out += "已建立連結。 rn";
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 + "rn";
}
file->Close();
delete file;
}
else
{
m_out+="finger查詢失敗。rn";
}
connection->Close();
delete connection;
}
else
{
m_out += "本地址沒有發現finger主機 。 rn";
}
m_out += "------------------------------------------------------rn";
UpdateData(FALSE);
}
本函式中,語句connection = session.GetGopherConnection(host,NULL,NULL,79);用於建立finger連結。隨後,我們創立一個文字檔案型別的Gopher位置用來操作伺服器返回的資訊:CGopherLocator locator = connection->CreateLocator(NULL, NULL, GOPHER_TYPE_TEXT_FILE);。使用該Gopher位置開啟檔案並使用一個for迴圈來讀出該檔案的頭20行,隨後將它顯示出來。
在OnQuery()函式的末尾加入以下行:TryFinger(m_host);。編譯程式,輸入地址whitehouse.gov,程式將會返回該伺服器的地址,從返回的資訊可知,出於安全考慮,該伺服器的其它finger服務已被取消了。如果你輸入的網址沒有提供finger服務,程式將有較長一段時間的延遲,最後彈出一個訊息框,通知使用者連結出現超時錯誤,單擊ok按鈕即可。
六、使用gopher協議傳送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 + "rn";
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 += "已建立連結。 rn";
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 + "rn";
}
file->Close();
delete file;
}
else
{
m_out+="Whois查詢失敗。rn";
}
connection->Close();
delete connection;
}
else
{
m_out += "Whois查詢失敗。rn";
}
m_out += "------------------------------------------------------rn";
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伺服器,以查詢其它型別的域。
在OnQuery()函式的末尾加入以下行:TryFinger(m_host);。重新編譯程式,現在,我們可以對結尾為.com的域進行whois查詢了。
至此,本程式已全部結束。當然,有興趣的讀者可以對它進一步擴充,以完成更多的功能。可以對WinInet類進行一些小小的擴充,以便能透過特定的埠訪問e-mail和news服務。此外,也可以連結到一些著名的網路搜擎上並提交查詢,使程式具有搜尋功能。這一切,就取決於你的創造力了。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-988582/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- MFC對COM介面編寫的支援分析 (轉)
- NO MFC - 使用 .log 除錯程式 (轉)除錯
- 查詢區分大小寫 (轉)
- 基於MFC的編寫執行緒快速入門 (轉)執行緒
- mfc程式流程 (轉)C程式
- 五種查詢Internet連線狀態[含IP]的方法 (轉)
- 使用Delphi,SDK編寫Windows簡單程式 (轉)Windows
- 【SQL】Oracle查詢轉換之物化檢視查詢重寫SQLOracle
- 使MySQL查詢區分大小寫(轉)MySql
- SQL查詢語句使用 (轉)SQL
- 查詢重寫
- 用Delphi編寫DelTree程式 (轉)
- 編寫一個 SQL 查詢來實現分數排名。SQL
- 在SDK程式中使用MFC中的輔助類 (轉)
- 用VC++編寫CGI程式 (轉)C++
- 用VB編寫抽獎程式 (轉)
- 編寫易讀的程式碼 (轉)
- 動畫程式編寫——DirectDraw之旅(3)(轉)動畫
- 查詢程式使用的cursor 數量
- es的複雜查詢測試,使用jest的dsl工具寫查詢語句
- 10 行 Python 程式碼寫的模糊查詢Python
- HTML + CSS + JS 利用郵編查詢 API 實現郵編查詢工具HTMLCSSJSAPI
- 程式查詢
- 使用 xunit 編寫測試程式碼
- 使用Python編寫猜拳小程式Python
- 使用JavaScript編寫的爬蟲程式JavaScript爬蟲
- 使用 intern 編寫測試程式碼
- 查詢轉換
- (轉載)編寫高效的jQuery程式碼jQuery
- 用Delphi編寫安裝程式(1) (轉)
- 用VB編寫標準CGI程式 (轉)
- 用 C++Builder 編寫 Tray 程式 (轉)C++UI
- 自己編寫安裝製作程式 (轉)
- 簡述MFC程式生與死 (轉)C程式
- T-SQL進階:超越基礎 Level 2:編寫子查詢SQL
- HTML+CSS編寫靜態網站-37 媒體查詢初探HTMLCSS網站
- 編寫 SQL 查詢:讓我們從基礎知識開始SQL
- spring data JPA 模糊查詢 --- 使用 LIKE --- 的寫法Spring