-->
| 摘要 这篇技术性文章讨论了如何利用microsoft win32网络函数创建一个网络浏览器。这篇文章的宗旨是让读者了解一些win32网络函数的作用、能力和使用范围,而不是为这些功能给出一个详细的文档。这篇文章所配合的surfbear样本应用程序使用win32网络函数从网络服务器上读取html文件,并把它们显示成原始的、没有经过格式化的文本。 介绍 不通过网络,你就无法了解我的一个朋友。计算机杂志已经在internet上设置了电子期刊,而本地的报纸也已经把整个段落都放到了网络上。事实上,许多报纸都在联机。每个人都有一个主页,甚至一些无家可归的人都有一个主页。虽然有许多关于网络的消息难免言过其实,但网络正在变成计算机整体的一部分已经是无庸置疑的了。 microsoft 已经介绍了microsoft win32网络函数来协助开发者把网络变成他们的应用程序的整体部分。这些新的功能简化了使用ftp(文件传输协议)、和http(超文本传输协议)访问网络。使用win32网络函数的开发者不需要对tcp/ip或windows 配件。对于一些最普通的操作,开发者不需要知道他们正在使用的某个协议的细节。 最终,win32网络函数将成为win32应用程序接口的一部分并且与基于windows的不同的平台一起发布。最初,win32网络函数将安装在一个叫做wininet.dll的再分布式动态链接库里。(来自microsoft网络软件开发工具包,其网址是:http://www.microsoft.com/inter/sdle/)。这属于网络开发工具包的一部分。 这篇文章说明了如何使用win32网络函数去创建一个简单的网络浏览器。它没有具体详细的讨论这些功能的细节,但对他们的用法和操作给出了一个演示。请参考网址是http://www.microsoft.com/intdev/sdk/docs/wininet的microsoft win32网络函数的主题,可以了解到全部的细节。 这篇文章是配合surfbear样本应用程序而创作的。surfbear是一个html文件。覆盖了这个过程种特定的网络部分,但它没有涉及与这个过程有关的用户接口问题或html文件的显示或操作问题。 注意:这篇文章是基于wininet.dll一个相当早的版本。很可能其中的参数名、标识名和函数名发生了改变。但是函数的范围和意图应该还是和这篇文章中描述的是一致的。 网络函数 最好的探讨win32网络函数的方法是直接进入代码。下面的代码是样本的代码,为了方便阅读,错误处理部分已经被删除掉了。 hinternet hnet = ::internetopen("msdn surfbear", pre_config_internet_access, null, internet_invalid_port_number, 0) ; hinternet hurlfile = ::internetopenurl(hnet, "http://www.microsoft.com", null, 0, internet_flag_reload, 0) ; char buffer[10*1024] ; dword 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 lpszproxyname internet_invalid_port_number, // 4 internet_port nproxyport 0 // 5 dword dwflags ) ; internetopen返回一个类型为hinternet的句柄。其他的win32网络函数把这个句柄当作一个参数。现在你不能把一个hinternet句柄用在类似于readfile之类的其他win32函数中。但是随着microsoft windows和microsoft windows nt网络支持的成熟,这一点在将来不是不可能实现的。 当你已经结束使用wein32网络函数时,你应该调用internetclosehandle释放internetopen分配的资源。使用microsoft基础类(mfc)的应用程序将从文件的构造程序里象征性地调用internetopen。绝大多数应用程序都将在每一进程里调用internetopen。 internetopen 的第一个参数lpszcallername指定正在使用网络函数的应用程序。当http协议使用时,这个名字将变成用户代理。 第二个参数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代理去访问web。cern代理是一个充当网关的web服务器并且能向要使用代理的服务器发送http请求。 gateway_internet_access允许连接到world wide 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 "http://www.microsoft.com", // 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 页都支持得到页尺寸。(例如:www.toystory.com和www.movielink.com不支持这个功能)另外,tcp/ip能传递的数据也比要求的要少。所以,你的应用程序应该处理着两种情况并且围绕internetreadfile循环直到结果为true同时*lpdwnumberofbytesread为0。 使用httpopenrequest,httpsendrequest和httpqueryinfo去打开文件http://www.microsoft.com/msdn/msdninfo的代码显示如下,错误检测已经被删除。 // open internet session. hinternet hsession = ::internetopen("msdn surfbear", pre_config_internet_access, null, internet_invalid_port_number, 0) ; // connect to www.microsoft.com. hinternet hconnect = ::internetconnect(hsession, "www.microsoft.com", 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 "www.microsoft.com", //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。第二个参数(设置成www.microsoft.com)提供了服务器的地址。注意,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 lpszobjectname 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(统一资源定位)。换而言之,如果你在www.home.com上而且单击了跳到www.microsoft.com的一个连接,第五个参数就是www.home.com。因为它使你指向了目标url(统一资源定位)。这个值可以为空。第六个参数执行一个我们的程序接收的文件类型列表。把空值传递给httpopenrequest即通知了服务器只有文本文件可以被接收。 httpsendrequest 除了传送请求外,httpsendrequest允许你传送额外的http标题给服务器。关于http标题的信息可以在http://www.w3.org/ 上的最新的说明上找到。在这个例子中,httpsendrequest的所有参数都被传递为缺省值。 bool bsendrequest = ::httpsendrequest( hhttpfile, // 1 hinternet hhttprequest
本文关键:利用Win32的网络函数创建一个网络浏览器
|