利用Win32的網路函式建立一個網路瀏覽器 (轉)

worldblog發表於2007-12-09
利用Win32的網路函式建立一個網路瀏覽器 (轉)[@more@]

利用的建立一個網路

Dale Rogerson

網路開發技術小組

摘要

這篇技術性文章討論瞭如何利用Microsoft Win32網路函式建立一個網路瀏覽器。這篇文章的宗旨是讓讀者瞭解一些Win32網路函式的作用、能力和使用範圍,而不是為這些功能給出一個詳細的文件。這篇文章所配合的SurfBear樣本應用使用Win32網路函式從網路上讀取HTML,並把它們顯示成原始的、沒有經過格式化的文字。

 

介紹

不透過網路,你就無法瞭解我的一個朋友。雜誌已經在inte上設定了電子期刊,而本地的報紙也已經把整個段落都放到了網路上。事實上,許多報紙都在聯機。每個人都有一個主頁,甚至一些無家可歸的人都有一個主頁。雖然有許多關於網路的訊息難免言過其實,但網路正在變成計算機整體的一部分已經是無庸置疑的了。

Microsoft 已經介紹了Microsoft Win32網路函式來協助開發者把網路變成他們的應用程式的整體部分。這些新的功能簡化了使用()、和HTTP(超文字傳輸協議)訪問網路。使用Win32網路函式的開發者不需要對或 配件。對於一些最普通的操作,開發者不需要知道他們正在使用的某個協議的細節。

最終,Win32網路函式將成為Win32應用程式介面的一部分並且與基於Windows的不同的平臺一起釋出。最初,Win32網路函式將在一個叫做WININET.DLL的再分散式動態連結庫裡。(來自Microsoft網路開發工具包,其網址是:)。這屬於網路開發工具包的一部分。

這篇文章說明了如何使用Win32網路函式去建立一個簡單的網路瀏覽器。它沒有具體詳細的討論這些功能的細節,但對他們的用法和操作給出了一個演示。請參考網址是/intdev/sdk/docs/wininet的Microsoft Win32網路函式的主題,可以瞭解到全部的細節。

這篇文章是配合SurfBear樣本應用程式而創作的。SurfBear是一個HTML檔案。覆蓋了這個過程種特定的網路部分,但它沒有涉及與這個過程有關的介面問題或HTML檔案的顯示或操作問題。

注意:這篇文章是基於WININET.DLL一個相當早的版本。很可能其中的引數名、標識名和函式名發生了改變。但是函式的範圍和意圖應該還是和這篇文章中描述的是一致的。

 

網路函式

最好的探討Win32網路函式的方法是直接進入程式碼。下面的程式碼是樣本的程式碼,為了方便閱讀,錯誤處理部分已經被刪除掉了。

HINTERNET = ::InternetOpen("MSDN SurfBear",
PRE_CONFIG_INTERNET_ACCESS,
NULL,
INTERNET_INVALID_PORT_NUMBER,
0) ;

HINTERNET hUrlFile = ::InternetOpenUrl(hNet,
"",
NULL,
0,
INTERNET_FLAG_RELOAD,
0) ;

char buffer[10*1024] ;
D dwBytesRead = 0;
BOOL bRead = ::InternetReadFile(hUrlFile,
buffer,
sizeof(buffer),
&dwBytesRead);

::InternetCloseHandle(hUrlFile) ;

::InternetCloseHandle(hNet) ;
上面列舉的程式碼包括四個網路函式:InternetOpen、InternetOpenOrl、InternetReadFile和InternetCloseHandle。下面我們依次對這些函式進行分析。

InternetOpen

InternetOpen初始化WININET.DLL。它在其他的Win32網路函式之前被。

HINTERNET hNet = ::InternetOpen(
"MSDN SurfBear", // 1 LPCTSTR lpszCallerName
PRE_CONFIG_INTERNET_ACCESS, // 2 DWORD dwAccessType
"", // 3 LPCTSTR lpszName
INTERNET_INVALID_PORT_NUMBER, // 4 INTERNET_PORT nProxyPort
0 // 5 DWORD dwFlags
) ;
InternetOpen返回一個型別為HINTERNET的控制程式碼。其他的Win32網路函式把這個控制程式碼當作一個引數。現在你不能把一個HINTERNET控制程式碼用在類似於ReadFile之類的其他Win32函式中。但是隨著Microsoft Windows和Microsoft 網路支援的成熟,這一點在將來不是不可能實現的。

當你已經結束使用Wein32網路函式時,你應該呼叫InternetCloseHandle釋放InternetOpen分配的資源。使用Microsoft基礎類(MFC)的應用程式將從檔案的構造程式裡象徵性地呼叫InternetOpen。絕大多數應用程式都將在每一程式裡呼叫InternetOpen。

InternetOpen 的第一個引數lpszCallerName指定正在使用網路函式的應用程式。當使用時,這個名字將變成使用者。

第二個引數dwAccessType指定訪問型別。在上面的例子裡,PRE_CONFIG_INTERNET_ACCESS訪問型別指示Win32網路函式使用登記資訊去發現一個伺服器。使用PRE_CONFIG_INTERNET_ACCESS需要正確設定登記資訊。這裡我耍了一個小花招並讓網路開發者替我登記註冊。如果你不想欺騙,你需要按圖1所示設定登記資訊。

 

 

 

 

在登記註冊中,把AccessType設定為1,則意味著“直接入網”,把AccessType 設定為2,意味著“使用閘道器”。把DisableServiceLocation設定為1,將讓它使用一個已經命名的伺服器;否則將找到一個使用註冊資訊和名字決議(RNR)應用程式介面的伺服器,它是Windows介面的一部分。

其他的訪問型別包括以下幾種:

LOCAL_INTERNET_ACCESS只連線到當地Internet網站。例如,如果我使用SurfBear標誌,我就只能訪問Microsoft整體的Internet網站。
CERN_PROXY_INTERNET_ACCESS使用一個CERN代理去訪問。CERN代理是一個充當閘道器的web伺服器並且能向要使用代理的伺服器傳送HTTP請求。
GATEWAY_INTERNET_ACCESS允許連線到World W Web。我可以用這個訪問型別去訪問web上的任何站點。
GATEWAY_PROXY_INTERNET_ACCESS和CERN_PROXY_ACCESS訪問型別要求第三個引數給InternetOpen:伺服器名(lpszProxyName)。PRE_CONFIG_INTERNET_ACCESS不要求伺服器名,因為他可以為伺服器搜尋寄存資訊。

NProxyPort引數用在CERN_PROXY_INTERNET_ACCESS中用來指定使用的埠數。使用INTERNET_INVALID_PORT_NUMBER相當於提供卻省的埠數。

最後一個引數棗dwFlags,設定額外的選擇。你可以使用 INTERNET_FLAG_ASYNC標誌去指示使用返回句控制程式碼的將來的Internet函式將為回撥函式傳送狀態資訊,使用InternetSetStatusCallback進行此項設定。

 

InternetOpenUrl

一旦你把Win32網路函式初始化了,你就可以使用其他網路函式。下一個要呼叫的Internet 函式是InternetOpenUrl。這個函式連線到一個網路伺服器上並且最被從伺服器上讀取資料。InternetOpenUrl能對FTP,Gopher或HTTP協議起作用。在這篇文章中,我們只涉及HTTP協議。

HINTERNET hUrlFile = ::InternetOpenUrl(
hNet, // 1 HINTERNET hInternetSession
"", // 2 LPCTSTR lpszUrl
NULL, // 3 LPCTSTR lpszHeaders
0, // 4 DWORD dwHeadersLength
INTERNET_FLAG_RELOAD, // 5 DWORD dwFlags
0 // 6 DWORD dwContext
) ;
InternetOpenUrl也返回一個HINTERNET,它被傳遞給在這個URL(統一資源定位)上操作的函式。你應該使用InternetClose來關閉這個控制程式碼的處理。

InternetOpenUrl的第一個引數hInternetSession是從InternetOpen返回的控制程式碼。第二個引數lpszUrl是我們需要的資源的URL(統一資源定位)。在上面的例子中,我們想得到一個Microsoft的web主頁。下面兩個引數lpszHeaders和HeaderLength用來向伺服器傳送額外的資訊。使用這些引數要求具有正在使用的特定協議的知識。

DwFlag是一個可以用幾種方式修改InternetOpenUrl行為的標誌,InternetOpenUrl的行為包括關閉、隱藏,使原始資料可用和用存在的連線取代開闢一個新的連線。

最後一個引數dwContext是一個 DWORD上下文值。如果有一個值已經被指定,它將被送到狀態回撥函式。如果這個值是0,資訊將不會被送到狀態回撥函式。

 

InternetReadFile

你開啟一個檔案後,就要讀它,所以下一個函式是InternetReadFile是符合邏輯的:

char buffer[10*1024] ;
DWORD dwBytesRead = 0;
BOOL bRead = ::InternetReadFile(
hUrlFile, // 1 HINTERNET hFile
buffer, // 2 LPVOID lpBuffer
sizeof(buffer), // 3 DWORD dwNumberOfBytesToRead
&dwBytesRead // 4 LPDWORD lpdwNumberOfBytesRead
);

buffer[dwBytesRead] = 0 ;
pEditCtrl->SetWindowText(buffer) ;
InternetReadFile接收InternetOpenUrl返回的控制程式碼。它也對其他Win32網路函式,例如FtpOpenFile,FopherOpenFile和HttpOpenRequest返回的控制程式碼有影響。

剩下的InternetReadFile的三個引數也非常的明白直接。Inbuffer是指向保留資料的緩衝區的一個無返回值指標,dwNumberOfByteToRead以位元組為單位指定緩衝區的尺寸。最後一個引數,lpdwNumberOfBytesRead是一個指向包含讀入緩衝區位元組數的變數的指標。如果返回值是TRUE,而且lpdwNumberOfBytesRead指向0,則檔案已經讀到了檔案的末尾。這個行為與Win32 Re3adFile的函式的行為是一致的。一個真正的web瀏覽器將在InternetReadFile上迴圈 ,不停地從Internet上讀入資料塊。

為了顯示緩衝區,向緩衝區新增一個0並把它送到編輯器控制。

這樣,InternetOpen、InternetOpenUrl和InternetReadFile一起建立了Internet瀏覽器的基礎。他們使從Internet上讀取檔案就象從你的本地器上讀取檔案一樣容易。

 

HTTP函式

在一些例子中,InternetOpenUrl太普通了,所以你可能需要其他的Win32網路函式。InternetOpenUrl相當與不同的FTP,GOPHER和HTTP函式的封皮。當使用HTTP時,InternetOpenUrl呼叫InternetConnect,HttpOpenRequest以及HttpSendRequest,比如說我們想要在一個HTML頁之前得到它的尺寸以便於我們在緩衝區中為其分配適當的尺寸,HttpQueryInfo將得到web頁的大小。

警告:不是所有web 頁都支援得到頁尺寸。(例如:和不支援這個功能)另外,TCP/IP能傳遞的資料也比要求的要少。所以,你的應用程式應該處理著兩種情況並且圍繞InternetReadFile迴圈直到結果為TRUE同時*lpdwNumberOfBytesRead為0。

使用HttpOpenRequest,HttpSendRequest和HttpQueryInfo去開啟檔案/msdn/msdninfo的程式碼顯示如下,錯誤檢測已經被刪除。

// Open Internet session.
HINTERNET hSession = ::InternetOpen("MSDN SurfBear",
PRE_CONFIG_INTERNET_ACCESS,
NULL,

INTERNET_INVALID_PORT_NUMBER,
0) ;

// Connect to
HINTERNET hConnect = ::InternetConnect(hSession,
"",
INTERNET_INVALID_PORT_NUMBER,
"",
"",
INTERNET_SERVICE_HTTP,
0,
0) ;

// Request the file /MSDN/MSDNINFO/ from the server.
HINTERNET hHttpFile = ::HttpOpenRequest(hConnect,
"GET",
"/MSDN/MSDNINFO/",
HTTP_VERSION,
NULL,
0,
INTERNET_FLAG_DONT_CACHE,
0) ;

// Send the request.
BOOL bSendRequest = ::HttpSendRequest(hHttpFile, NULL, 0, 0, 0);

// Get the length of the file.




char bufQuery[32] ;
DWORD dwLengthBufQuery = sizeof(bufQuery);
BOOL bQuery = ::HttpQueryInfo(hHttpFile,
HTTP_QUERY_CONTENT_LENGTH,

bufQuery,

&dwLengthBufQuery) ;

// Convert length from ASCII string to a DWORD.
DWORD dwFileSize = (DWORD)atol(bufQuery) ;

// Allocate a buffer for the file.



char* buffer = new char[dwFileSize+1] ;

// Read the file into the buffer.

DWORD dwBytesRead ;
BOOL bRead = ::InternetReadFile(hHttpFile,
buffer,
dwFileSize+1,

&dwBytesRead);
// Put a zero on the end of the buffer.
buffer[dwBytesRead] = 0 ;

// Close all of the Internet handles.
::InternetCloseHandle(hHttpFile);

::InternetCloseHandle(hConnect) ;
::InternetCloseHandle(hSession) ;

// Display the file in an edit control.
pEditCtrl->SetWindowText(buffer) ;

InternetConnect
InternetConnet函式連線到一個HTTP,FTP或Gopher伺服器:
HINTERNET hConnect = ::InternetConnect(
hSession, //1 HINTERNET hInternetSession
"", //2 LPCTSTR lpszServerName
INTERNET_INVALID_PORT_NUMBER, //3 INTERNET_PORT nServerPort
"", //4 LPCTSTR lpszUsername
"", //5 LPCTSTR lpszPassword
INTERNET_SERVICE_HTTP, //6 DWORD dwService
0, //7 DWORD dwFlags
O //8 DWORD dwContext
) ;
第六個引數dwService決定服務型別(HTTP,FTP或Gopher)。在上面的例子中,InternetConnect連線到一個HTTP伺服器上,因為dwService被設定成INTERNET_SERVICE_HTTP。第二個引數(設定成)提供了伺服器的地址。注意,HTTP地址必須為伺服器名作語法分析,InternetOpenUrl為我們作語法分析。第一個引數hInternetSession是從InternetOpen返回的控制程式碼。第四個、第五個引數提供一個使用者姓名和密碼 。這七個引數沒有控制任何標誌影響HTTP操作。最後一個引數為狀態回撥函式提供前後關係的資訊。

HttpOpenRequest
一旦和伺服器的連線已經建立,我們開啟了想要的檔案。HttpOpenRequest和HttpSenRequest一起工作開啟檔案。HttpOpenRequest去建立一個請求控制程式碼並且把引數在控制程式碼中。HttpOpenRequest把請求引數送到HTTP伺服器。
HINTERNET hHttpFile = ::HttpOpenRequest(
hConnect, // 1 HINTERNET hHttpSession
"GET", // 2 LPCTSTR lpszVerb
"/MSDN/MSDNINFO/", // 3 LPCTSTR lpszName
HTTP_VERSION, // 4 LPCTSTR lpszVersion
NULL, // 5 LPCTSTR lpszReferer
0, // 6 LPCTSTR FAR * lplpszAcceptTypes
INTERNET_FLAG_DONT_CACHE, // 7 DWORD dwFlags
0 // 8 DWORD dwContext
) ;
到現在為止,網路函式的許多引數看起來都類似。HttpOpenResult的第一個引數是由InternetConnet返回的 HINTERNET。HttpOpenRequest的第七和第八個引數與InternetConnect中有相同名字的引數一樣的功能。
第二個引數(“GET”)指定我們想要得到由第三個引數(“/MSDN/MSDNINFO/”)命名的。HTTP版已經傳遞第四個引數;現在,它肯定是HTTP棗VERSION。因為“GET”是最流行的動詞型別,HttpOpenRequest將為這個引數接收一個空指標。
第五個引數lpszReferer是一個網點的地址。在這個網點上我們發現了我們現在想要看見的URL(統一資源定位)。換而言之,如果你在上而且單擊了跳到的一個連線,第五個引數就是。因為它使你指向了目標URL(統一資源定位)。這個值可以為空。第六個引數執行一個我們的程式接收的檔案型別列表。把空值傳遞給HttpOpenRequest即通知了伺服器只有文字檔案可以被接收。

 

HttpSendRequest

除了傳送請求外,HttpSendRequest允許你傳送額外的HTTP標題給伺服器。關於HTTP標題的資訊可以在 上的最新的說明上找到。在這個例子中,HttpSendRequest的所有引數都被傳遞為預設值。

BOOL bSendRequest = ::HttpSendRequest(
hHttpFile, // 1 HINTERNET hHttpRequest
NULL, // 2 LPCTSTR lpszHeaders
0, // 3 DWORD dwHeadersLength
0, // 4 LPVOID lpOptional
0 // 5 DWORD dwOptionalLength
);
HttpQueryInfo

為了得到關於檔案的資訊,在呼叫HttpSendRequest後使用HttpQueryInfo函式:

BOOL bQuery = ::HttpQueryInfo(
hHttpFile, // 1 HINTERNET hHttpRequest
HTTP_QUERY_CONTENT_LENGTH, // 2 DWORD dwInfoLevel
bufQuery, // 3 LPVOID lpvBuffer
&dwLengthBufQuery // 4 LPDWORD lpdwBufferLength
) ;
查詢的結構是字串或lpvBuffer中的字串列表。HTTP_QUERY_CONTENT_LENGTH查詢得到檔案的長度。你可以使用HttpQueryInfo查詢大範圍的資訊。要獲知詳細情形可查閱網點/intdev/sdk/docs/wininet上的Microsoft Win32網路函式專題。

SurfBear樣本應用程式

SurBear樣本應用程式使用Win32網路函式從Internet上得到檔案並且在編輯器上顯示原始的HTML格式。SurfBear使用HttpOpenRequest和HttpSendRequest取代InternetOpenUrl,純粹是為了演示的需要。

 

圖2 SurfBear 螢幕

SurfBear是一個MFC4.0版本的對話應用程式。它所有與Internet有關的功能都在InternetThread.h和InternetThread.cpp檔案中。

從internet上讀取檔案要花費相當數量的時間,所以從一個工作執行緒呼叫網路函式是一個明智的主意。透過這種方式,當在等待得到資料時,應用程式的視窗能被改變尺寸和移動。

圖3顯示了SurfBear的控制流。

 

 

當使用者按下GOTO按鈕時,CsurfBearDlg::OnBtnGoto呼叫CinternetThread:GetWebPoge,傳遞想要的web頁的HTTP地址。GetWebPage把HTTP地址語法分析成伺服器和物件名,儲存在CinternetThread的成員變數中。GetWebPage然後呼叫AfxBeginThread,它產生一個執行靜態成員函式的執行緒GetWebPage WorkerThread。如果網路函式沒有被初始化,GetWebPageWorkerThread呼叫InternetOpen,然後它嘗試讀取想要的web頁。當GetWebPageWorkerThread結束時,它傳送一個使用者定義的WM_READFILECOMPLETED訊息給SurfBear對話方塊。OnReadFileCompleted處理這個訊息並且把一個web頁複製到編輯器控制裡。

總結

Win32網路函式使從FTP,Gopher和HTTP伺服器上讀取資訊就想從你的硬碟驅動器上讀取資訊一樣容易。僅僅使用4個函式棗InternetOpen,InternetOpenUrl,InternetReadFile和InternetCloseHandle和很少的HTTP知識,你就可以寫一個簡單的網路瀏覽器。

把這個簡單的瀏覽器變成一個工業性質的瀏覽器將要花費很多工作,包括一些對HTTP的瞭解,對如何顯示HTML檔案的瞭解和以及使用多執行緒方式的能力。Win32網路函式將開發者從與TCP/IP,Windows Sockets和HTTP有關的大多數煩悶工作中解脫出來。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-990600/,如需轉載,請註明出處,否則將追究法律責任。

相關文章