版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第12章应用层网络编程12.1SMTP/POP3 协议编程12.2HTTP编程12.3FTP编程12.4SNMP编程小结
网络通信的低层协议开发总是繁琐和复杂的,其根本目标最终还是服务于工作在应用层上的进程,因此应用层网络编程才是终极目标。实现应用层的编程方法大体可以分为两种,即按照应用层协议使用通用编程接口和调用应用层协议专用编程接口。前者是前面花费大量篇幅所介绍知识的深入运用,后者是借助于开发平台提供商对低层接口进行封装的一种便捷手段。本章结合具体的应用层协议,对这两种方法分别进行了介绍。本章从FTP协议入手,首先采用基于WinSockAPI的最原始接口,讲解编程实现SMTP/POP3应用编程;接着使用微软提供的WinInet实现基于HTTP和FTP的应用层协议编程;最后使用SNMPAPI实现基于SNMP的应用层协议编程。服务器的设计可重点参阅第8章的完成端口技术,本章则更加侧重于客户端的编程。
互联网电子邮件(ElectronicMail,E-mail)提供了快速传递信息的功能,是现在人们在互联网上最常用的远程网络通信工具。在TCP/IP协议中,完成该服务的协议主要是SMTP和POP3。12.1SMTP/POP3协议编程12.1.1SMTP/POP3协议简介
1.SMTP
SMTP(SimpleMailTransferProtocol),即简单邮件传输协议,它的目标是可靠高效地传送邮件,它独立于传送子系统且仅要求一条可以保证传送数据单元顺序的通道。SMTP的一个重要特点是它能够接力传送邮件,传送服务提供了进程间通信环境(IPCE),此环境可以包括一个网络、几个网络或一个网络的子网。因此,SMTP通常工作在两种情况下:一是电子邮件从客户端传输到服务器;二是从某一个服务器传输到另一个服务器。
SMTP设计基于以下通信模型:针对用户的邮件请求,“发送SMTP进程”与“接收SMTP进程”之间建立一个双向传送通道。接收SMTP的可以是最终接收者,也可以是中间传送者。SMTP命令由发送SMTP发出,由接收SMTP接收,而应答则反方面传送。一旦传送通道建立,SMTP发送者就发送MAIL命令指明邮件发送者。如果SMTP接收者可以接收邮件,则返回OK应答。SMTP发送者再发出RCPT命令确认邮件是否接收到。如果SMTP接收者接收,则返回OK应答;如果不能接收到,则发出拒绝接收应答(但不终止整个邮件操作),双方将如此重复多次。当接收者收到全部邮件后会接收到特别的序列,如果接收者成功处理了邮件,则返回OK应答。SMTP提供传送邮件的机制,当接收方与发送方连接在同一个传送服务下时,邮件可以直接由发送方主机传送到接收方主机;或者,当两者不在同一个传送服务下时,通过中继SMTP服务器传送。为了能够给SMTP服务器提供中继能力,它必须拥有最终目的主机地址和邮箱名称。
由此可见,SMTP是一个命令(请求)/响应协议。SMTP命令和响应都是基于ASCII文本的,并以CR和LF符结束。响应包括一个表示返回状态的三位数字代码。常见的SMTP命令与响应分别如表12-1和表12-2所示。表12-1SMTP的基本命令表12-2SMTP服务器响应码应答码由三位构成,每一位都有特定的意义。第一位应答表示是成功的、失败的或未完成的。通过这一位,不复杂的SMTP发送就可以容易地决定下一步的操作,如果发送方希望大概了解究竟出了什么问题,它可以检测第二位,而第三位则保存了最后更完整的信息。也就是说,从第一位到第三位,接收方可以一步比一步精确地确定接收方的状态。
SMTP是在TCP协议的25号端口监听连接请求的。为了加深对命令(请求)/响应协议以及SMTP的理解,下面列举一个简单的SMTP的连接和发送过程描述:
(1)建立TCP连接。
(2)客户端先发送HELO命令以标识发件人自己的身份,然后发送MAIL命令,服务器端以OK作为响应,表明准备接收。
(3)客户端发送RCPT命令,以标识该电子邮件的计划接收人,可以有多个RCPT行,服务器端则表示是否愿意为收件人接受邮件。
(4)协商结束,发送邮件,用命令DATA发送。
(5)以“.”表示结束输入内容一起发送出去。
(6)结束此次发送,用QUIT命令退出。表12-3SMTP会话过程为了了解编程前服务器端的状态,可以利用Telnet远程登录我们SMTP客户端程序对应的主机观察邮件传输的会话过程。以下是通过Telnet连接SMTP邮件服务器“eqmanager@.”发送一封电子邮件的SMTP协议交互过程,其中用户名为“hellowxd”,密码为“888888”。了解了这一交互过程,对于设计SMTP客户端程序具有重要的参考价值,具体如图12-1所示。图12-1Telnet连接SMTP服务器需提请注意的是,目前很多黑客与病毒把SMTP的25端口作为窥探服务器内部或传播病毒的通道,因此25端口上的操作被认为非常危险。为了避免25端口上的安全隐患,一方面,SMTP服务器增加了身份认证的功能(如前述),另一方面许多安全软件还限制或屏蔽了对远端服务器25端口的连接。为了使得Telnet过程不受影响,除了要有邮件服务器上合法的用户名和密码之外,还应当关闭安全软件对25端口的监控。
关于SMTP的更多介绍,可以参阅RFC821。
2.POP3
SMTP是与POP(PostOfficeProtocol,邮局协议)联合使用的。POP用于接收电子邮件,适用于C/S结构的脱机模型的电子邮件协议,目前已发展到第三版,称为POP3。
POP3工作时,当客户机与服务器连接并查询新电子邮件时,被该客户机指定的所有将被下载的邮件都将被程序下载到客户机,下载后,电子邮件客户机就可以删除或修改任意邮件,而无需与电子邮件服务器进一步交互。POP3在TCP/110端口上等待客户连接请求。POP3客户向POP3服务器发送命令并等待响应,POP3命令采用命令行形式,用ASCII码表示。服务器响应由一个单独的命令行或多个命令行组成,响应的第一行以ASCII文本“+OK”或“-ERR”指出相应的操作状态是成功还是失败。在POP3协议中有三种状态,即认可、处理和更新状态。当客户机与服务器建立联系时,一旦客户机提供了自己身份并成功确认,即由认可状态转入处理状态,在完成相应的操作后客户机发出quit命令,则进入更新状态,更新之后最后重返认可状态。
SMTP和POP3配合工作的过程如图12-2所示。图12-2TCP/IP互联网上电子邮件的传输过程
12.1.2Base64编码
E-mail只能传送ASCII码格式的文字信息,而ASCII码是7位代码,非ASCII码格式的文件在传送过程中就需要先编成7位的ASCII代码,然后才能通过E-mail进行传送;如果不经过编码,则在传送过程中会因为ASCII码7位的限制而被分解,分解之后收信方将无法正确解释ASCII字符,从而导致邮件传输失败。在互联网上,由于产生时间、生产厂家和使用协议的不同,对传输数据的编码各不相同,最主要的编码方式有UU编码(Unix-to-Unixencoding)、MIME标准(MultipurposeInternetMailExtensions)等UU编码解决了E-mail只能传送ASCII文件的问题,但这种方式其实并不是很方便,因而,在Internet上使用一种新的编码标准MIME(MultipurposeInternetMailExtensions),一般译为“多媒体邮件传送模式”。顾名思义,它可以传送多媒体文件,在一封电子邮件中附加各种格式文件一起送出。
MIME定义了两种编码方式:QP(Quote-Printable)与Base64。QP的规则是:不对文件中的数据进行特殊编码,仅将8位的数据转成7位。QP编码适用于非ASCII码的文字内容,例如中文字符文件。而Base64的编码规则是将整个文件重新编码成小于或等于7位的数据,通常用于传送二进制文件。编码的方式不同会影响编码之后的文件大小。具有MIME功能的E-mail软件大都能自动判别邮件采用何种编码,然后自动选择用QP或Base64来解码。
QP编码是将一个字节用两个十六进制数值表示,然后在前面加“=”。如一段QP编码后的数据为=A4=AE=A6=A1=A7=DA=AC=B1=E7=A9=A7=A1=AB=DC=B0=AA=B
Base64编码后的文字通常为
PGquYaZuoUmn2qxPseepc6dnoUGr3LCqv70ms773t3ymYqa5plakaq5hptu
通过上述方法,邮件就可以传输二进制数了,包括声音、图像、文本及可执行文件。收信人通过支持MIME的软件,把编码后的文件翻译过来,读者在了解Base64的原理后,可以很容易地编程实现。
12.1.3SMTP客户端设计
在了解了SMTP的工作原理后,本节进行一个简单的SMTP客户端程序的实现。
本程序固化了SMTP的会话过程,假设邮件由me@发往him@,并带有一个用户设定路径的文件为附件。编码采用Base64编码方式。
#include<winsock2.h>
#include<string.h>
#include<stdio.h>
constintBASE64_MAXLINE=76;
constcharEOL[]="\r\n";
constcharBASE64_TAB[]="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz0123456789+/";
constcharHEADER[]=
"HELO\r\n"
//"AUTHLOGIN\r\n"//+BASE64USER+BASE64PASS①
"MAILFROM:me@\r\n"
"RCPTTO:him@\r\n"
"DATA\r\n"
"FROM:me@\r\n"
"TO:him@\r\n"
"SUBJECT:thisisatest\r\n"①有些SMTP服务器在进行身份认证时,要求对用户名和密码进行基于Base64的编码处理,在进行测试时可利用诸如/tools/base64.aspx类似的Based64在线编码器的帮助下,完成该项工作。
"Date:2011-5-18\r\n"
"X-Mailer:shadowstar'smailer\r\n"
"MIME-Version:1.0\r\n"
"Content-type:multipart/mixed;boundary=\"#BOUNDARY#\"\r\n"
//"Content-Type:text/plain;charset=gb2312\r\n"
"\r\n";
constcharCONTENT[]=
"\r\n--#BOUNDARY#\r\n"
"Content-Type:text/plain;charset=gb2312\r\n"
"Content-Transfer-Encoding:quoted-printable\r\n"
"\r\n"
"/*************************************************************"
"*UseSMTPtosendaneMailwithanAttachmentandverify*"
"*************************************************************"
"*/\r\n"
"\r\n";
constcharATT_HEADER[]=
"\r\n--#BOUNDARY#\r\n"
"Content-Type:application/octet-stream;name=smtp.exe\r\n"
"Content-Disposition:attachment;filename=smtp.exe\r\n"
"Content-Transfer-Encoding:base64\r\n"
"\r\n";
intANSIToBase64(constchar*szInANSI,intnInLen,char*szOutBase64,intnOutLen){…};\\略
intmain(intargc,char*argv[])
{
WSADATAwsaData;
intSockFD;
structsockaddr_inServAddr;
charbuf[0x100];
intx;
FILE*fp;
char*aatt=newchar[0x400000];
char*batt=newchar[0x555556];
//①套接字初始化
WSAStartup(MAKEWORD(2,2),&wsaData);
LPHOSTENTpHost=gethostbyname("");
SockFD=socket(AF_INET,SOCK_STREAM,IPPROTO_TCP);
ServAddr.sin_family=AF_INET;
ServAddr.sin_addr.s_addr=*(ULONG*)pHost->h_addr_list[0];
ServAddr.sin_port=htons(25);
//②连接邮件服务器并发送邮件头和内容
connect(SockFD,(structsockaddr*)&ServAddr,sizeof(ServAddr));
send(SockFD,HEADER,strlen(HEADER),0);//sendHEADER
send(SockFD,CONTENT,strlen(CONTENT),0);//sendCONTENT
send(SockFD,ATT_HEADER,strlen(ATT_HEADER),0);//sendATT_HEADER
//③打开本地文件作为邮件附件
fp=fopen(argv[0],"rb");//readattachment
fseek(fp,0,2);
x=ftell(fp);
if(x>0x400000)x=0;
rewind(fp);
fread(aatt,x,1,fp);
fclose(fp);
x=ANSIToBase64(aatt,x,batt,0x555556);
//④发送邮件附件
send(SockFD,batt,x,0);//sendbase64attachment
send(SockFD,".\r\n",strlen(".\r\n"),0);//end
send(SockFD,"QUIT\r\n",strlen("QUIT\r\n"),0);//quit
//⑤关闭套接字
closesocket(SockFD);
WSACleanup();
delete[]aatt;
delete[]batt;
return0;
}
本程序实现了基本的SMTP操作(程序中归纳为五步),与实际的邮件服务客户端在功能、界面等方面还相差甚远,但基本反映了邮件客户端的基本骨架。
直接使用POP3设计程序的用户较少,编程方法与上述程序类似,这里就不做过多介绍。
互联网之所以能够发展成为万维网,是因为它提供了一种更加方便的信息浏览方式,这就是Web。Web技术基于TCP/IP协议中的HTTP,更加注重信息的描述本身,而封装了与繁琐的TCP进程相关的维护。12.2HTTP编程
12.2.1HTTP简介
HTTP(HyperTextTransferProtocol)即超文本传输协议,是一种用于从Web服务器端传送超文本标记语言文件到客户端浏览器的传输协议,它是Internet上常见的协议之一。我们通常访问的网页,就是通过HTTP协议进行传送的。HTTP用名字标识某一资源时(即在浏览器中输入网址),遵循统一资源名(UniformResourceName,URN)的规则,当前网络中最常用的URN是统一资源定位符(UniformResourceLocator,URI),当客户端在浏览器中输入一个URL或单击一个URL超链接时,就确定了要访问的地址。以/
resource/index.html为例介绍URL的组成,其中:
http://:表示使用HTTP协议;
www:代表1个Web服务器;
/:Web服务器的域名或站点服务器的名称;
resource/:Web服务器上的子目录,类似机器中的文件夹;
index.html:Web服务器上resource子目录中的一个网页文件,即Web服务器传送给客户端浏览器的文件。
HTTP使用TCP协议的80端口进行可靠数据传输,一个HTTP会话由客户端开始发起,包括以下4个步骤(如图12-3所示)。
图12-3HTTP会话过程
(1)客户机与WWW服务器或代理服务器建立连接(TCP)。
(2)客户机发送一个请求给WWW服务器或代理服务器,请求的格式为:请求命令符
+
URL
+
协议版本号+MIME信息(含请求修饰符、客户机信息和其他可能的内容)。
(3)
WWW服务器或代理服务器接到请求后,给予响应信息,其格式为:状态行
+
响应码
+
MIME信息(含服务器信息、实体信息和其他可能的内容)。
(4)客户端接收WWW服务器或代理服务器返回的信息,并通过浏览器显示在用户的屏幕上。
Http协议的请求命令如表12-4所示。所有命令必须全为大写,否则服务器会认为是不可识别的。表12-4Http协议请求命令对于上述请求命令,服务器会根据具体情况返回响应码,这与12.1.1节介绍的SMTP协议非常类似,响应码的含义如表12-5所示。表12-5HTTP请求响应码
12.2.2WinInetAPI
WinInet是一个网络编程接口,包含了对Internet底层协议HTTP、FTP、Gopher(已不太使用)的访问。借助WinInet接口,不必了解WinSock、TCP/IP和特定Internet协议的细节就可以编写出高水平的Internet客户端程序。WinInet为HTTP、FTP、Gopher提供了统一的函数集,也就是Win32API接口,从而简化了这几种协议的编程。
1.Hinternet句柄
进行WinInetAPI客户端程序设计时需要使用一个特殊类型的句柄,即Hinternet。Hinternet不同于常规的Win32句柄,为WinInet所特有。Hinternet句柄由WinInetAPI函数创建,并且不同的函数所创建的Hinternet句柄之间还存在着承接关系,必须遵循这种承接关系进行嵌套引用。最高级别的Hinternet句柄由InternetOpen()函数创建,下一级别的Hinternet句柄创建函数(包括InternetOpenUrl()和InternetConnect())必须引用这个句柄来创建,依次类推,图12-4说明了Hinternet句柄的承接关系。图12-4Hinternet句柄的承接关系基于WinInetAPI的HTTP、FTP的编程步骤是:首先,通过InternetOpen()函数创建位于根部的Hinternet句柄,通过其进一步建立Http、FTP的连接;然后,使用InternetConnect()函数创建一个指定的连接,该连接将通过传递给它的参数为指定的站点初始化HTTP、FTP连接并创建一个从根句柄分支出去的Hinernet句柄;接着,HttpOpenRequest()和FtpOpenFile()、FtpFindFirstFile()等函数将使用InternetConnect()所创建的句柄以建立到指定站点的连接来进行数据的交互;最后,关闭Hinternet句柄,释放资源。
2.HTTP函数
一个基于WinInetAPI的HTTP的完整编程需要用到以下函数。
1)
WinInet初始化函数
HINTERNETInternetOpen(INLPCSTRlpszAgent, //应用程序名,可以自定义
INDWORDdwAccessType, //存取类型
INLPCSTRlpszProxyName, //CERN代理服务器地址
INLPCSTRlpszProxyBypass, //代理服务器地址
INDWORDdwFlags); //标记,一般设置为0
其中,参数dwAccessType可以是INTERNET_OPEN_TYPE_PRECONFIG,表示使用IE中的连接设置;也可以是INTERNET_OPEN_TYPE_DIRECT,表示直接连接到服务器;或
者是INTERNET_OPEN_TYPE_PROXY,表示通过代理服务器进行连接。参数dwFlags可以是INTERNET_FLAG
_DONT_CACHE,表示不在缓存中保存取得的内容;或是INTERNET_FLAG_OFFLINE,表示脱机方式。
2)打开Url来读取数据函数
HINTERNETInternetOpenUrl(INHINTERNEThInternetSession, //由InternetOpen返回的句柄
INLPCSTRlpszUrl, //文件的Url地址
INLPCSTRlpszHeaders, //发送到服务器的数据头
INDWORDdwHeadersLength, //发送到服务器的数据头长度
INDWORDdwFlags, //标记
INDWORDdwContext); //上下文标记其中,参数dwFlags可以是INTERNET_FLAG_RELOAD,表示强制重读数据;或是INTERNET_FLAG_DONT_CACHE,表示不保存到缓存;或是INTERNET_FLAG_TRANSFER_ASCII,表示使用文本数据;或是INTERNET_FLAG_TRANSFER_BINARY,表示使用二进制数据。
3)建立Internet的连接函数
HINTERNETInternetConnect(INHINTERNEThInternetSession, //由InternetOpen返回的句柄
INLPCSTRlpszServerName, //服务器的地址HTTP地址
ININTERNET_PORTnServerPort, //HTTP协议端口号(缺省80)
INLPCSTRlpszUsername, //用户名
INLPCSTRlpszPassword, //用户密码
INDWORDdwService, //决定服务类型
INDWORDdwFlags, //标识
INDWORDdwContext); //上下文
其中,参数dwService可以是INTERNET_SERVICE_FTP,表示连接到一个FTP服务器;也可以是INTERNET_SERVICE_HTTP,表示连接到一个HTTP服务器。
4)打开一个HTTP请求的句柄函数
HINTERNETHttpOpenRequest(INHINTERNEThHttpSession, //InternetConnect句柄
INLPCSTRlpszVerb, //命令字
INLPCSTRlpszObjectName, //命令对象
INLPCSTRlpszVersion, //HTTP版本,缺省为“HTTP/1.0”
INLPCSTRlpszReferer, //一个网址,可以为空
INLPCSTRFAR*lpszAcceptTypes,//程序接收的文件类型列表
INDWORDdwFlags, //标识
INDWORDdwContext); //上下文
其中,参数dwFlags可以使用or连接多个标志,包括INTERNET_FLAG_NO_CACHE_WRITE,表示不缓冲写;INTERNET_FLAG_KEEP_CONNECTION,表示保持连接;INTERNET_
FLAG_SECURE,表示使用PCT/SSL。
5)向HTTP服务器发送指定的请求函数
BOOLHttpSendRequest(INHINTERNEThHttpRequest, //HttpOpenRequest句柄
INLPCSTRlpszHeaders, //服务请求的数据头
INDWORDdwHeadersLength, //服务请求的数据头的长度
INLPVOIDlpOptional, //标题后任意数据的地址,用于
//POST和PUT操作
DWORDdwOptionalLength); //数据的长度
6)设置一个Internet选项函数
BOOLInternetSetOption(INHINTERNEThInternet, //句柄
INDWORDdwOption, //Internet选项
INTERNET_OPTION_SEND_TIMEOUT //发送请求和连接时的
//超时时间
INTERNET_OPTION_RECEIVE_TIMEOUT, //接收请求和连接时的
//超时时间
INLPVOIDlpBuffer, //缓存区
INDWORDdwBufferLength); //缓存区大小其中,参数dwOption可以是INTERNET_OPTION_SEND_TIMEOUT,表示发送请求和连接时的超时时间;或者是INTERNET_OPTION_RECEIVE_TIMEOUT,表示接收请求和连接时的超时时间。
关于其他更多的WinInetAPI函数这里不作过多的介绍,可以参考MSDN。
12.2.3HTTP客户端设计
本节介绍一个基于WinInetAPI函数的HTTP客户端程序设计。该程序按照典型的HTTP客户端程序的处理流程设计,简化操作流程可分为七个步骤:①调用InternetOpen建立一个HINTERNET会话句柄;②调用InternetConnect建立与HTTP服务器的连接;③调用HttpOpenRequest初始化下载网页的请求;④调用HttpSendRequert发送一个HTTP请示;⑤调用HttpQueryInfo获取HTTP请求信息;⑥调用InternetReadFile读文件;⑦依次调用InternetCloseHandle结束HTTP会话。以下就是该wininethttp2.cpp文件的程序代码。
#include"stdafx.h"
#include"urlmon.h"
#include"wininet.h"
#pragmacomment(lib,"wininet.lib")
#pragmacomment(lib,"urlmon.lib")
typedefHRESULTSTDAPICALLTYPESHOWHTMLDIALOGFN(HWNDhwndParent,
IMoniker*pmk,VARIANT*pvarArgIn,TCHAR*pchOptions,VARIANT*pvArgOut);
char*buffer;
FILE*fp;
BOOLShowHtml()
{
SHOWHTMLDIALOGFN*pfnShowHTMLDialog;
HINSTANCEhinstMSHTML=LoadLibrary(TEXT("MSHTML.DLL")); //装载动态链接库
pfnShowHTMLDialog=
(SHOWHTMLDIALOGFN*)GetProcAddress(hinstMSHTML,TEXT("ShowHTMLDialog"));
if(pfnShowHTMLDialog==NULL)returnFALSE;
WCHAR*url=L"c:\\tempweb.html"; //此文件名可直接用地址名称代替
if(hinstMSHTML) //装载动态链接库成功
{
SHOWHTMLDIALOGFN*pfnShowHTMLDialog;
pfnShowHTMLDialog=
(SHOWHTMLDIALOGFN*)GetProcAddress(hinstMSHTML,TEXT("ShowHTMLDialog"));
if(pfnShowHTMLDialog)
{//根据URL得到IMoniker*变量,需使用urlmon.lib
IMoniker*moniker=NULL;
if(FAILED(CreateURLMoniker(NULL,(LPWSTR)url,&moniker)))
{
FreeLibrary(hinstMSHTML);
returnFALSE;
}
char*pOptions=
(char*)L"dialogHeight:768px;dialogWidth:1024px;help:yes;status:yes;scroll:yes";
//这里必须是UNICODE型的字符串,否则更改不了窗口的大小
pfnShowHTMLDialog(NULL,moniker,NULL,pOptions,NULL);
//调用ShowHTMLDialog函数显示临时文件中的网页
if(moniker!=NULL)moniker->Release();
returnTRUE; //显示成功,返回TRUE
}
elsereturnFALSE; //GetProcessAddress失败
FreeLibrary(hinstMSHTML);
}
elsereturnFALSE; //装载动态链接库失败
}
BOOLopenwebpage()
{
//①建立会话
HINTERNEThSession=::InternetOpen("MYHTTPclient",PRE_CONFIG_INTERNET_ACCESS,
NULL,INTERNET_INVALID_PORT_NUMBER,0);
//②建立连接
HINTERNEThConnect=::InternetConnect(hSession,"",
//服务器名,记住不要加http://
INTERNET_INVALID_PORT_NUMBER,"","",INTERNET_SERVICE_HTTP,0,0);
//③初始化下载请求
HINTERNEThHttpFile=::HttpOpenRequest(hConnect,"GET","",//填写欲打开的网页名
HTTP_VERSION,NULL,0,INTERNET_FLAG_DONT_CACHE,0);
//④发送下载请求
BOOLbSendRequest=::HttpSendRequest(hHttpFile,NULL,0,0,0);
//⑤获取文件长度
charbufQuery[32];
DWORDdwLengthBufQuery=sizeof(bufQuery);
BOOLbQuery=::HttpQueryInfo(hHttpFile,HTTP_QUERY_CONTENT_LENGTH,
bufQuery,&dwLengthBufQuery,NULL);
//转换文件的下载页面长度从ASCII字符串到DWORD.
DWORDdwFileSize=(DWORD)atol(bufQuery);
//为文件分配长度
buffer=newchar[dwFileSize+1];
//⑥读取文件到内存
DWORDdwBytesRead;
BOOLbRead=::InternetReadFile(hHttpFile,buffer,dwFileSize+1,&dwBytesRead);
//内存末尾加入0字符
buffer[dwBytesRead]=0;
//将下载的网页写入临时文件
fp=fopen("c:\\tempweb.html","wt+");
intnum=fwrite(buffer,sizeof(char),dwFileSize+1,fp);
fclose(fp);
//⑦关闭所有Internet句柄
::InternetCloseHandle(hHttpFile);
::InternetCloseHandle(hConnect);
::InternetCloseHandle(hSession);
returnTRUE;
}
intmain(intargc,char*argv[])
{
openwebpage(); //打开并下载网页
ShowHtml(); //启动显示html的对话框
return0;
}上述程序将目标网页下载到本地,调用了一个网页显示对话框将下载的文件用分辨率为1024
×
768的、带滚动条的窗口显示出来,显示工作是由ShowHtml实现的。可以说,这个程序就是一个简单的浏览器。如果在此程序上添加自动URL定位分析、多线程爬虫
(又称网页蜘蛛,即spider)、网页字符分析、倒排序表建立、检索反馈等模块,就又变成了网络搜索引擎。
由于WinInetAPI短小精悍、效率高,因此目前在Wince平台下应用的也很多,主要用于手机浏览器等应用的设计。
基于文件传输服务就是让用户连接上一个远程计算机,并查看远程计算机有哪些文件,然后把文件从远程计算机上复制到本地计算机,或把本地计算机的文件传送给远程计算机。这种服务为网络用户的文件共享提供了极大的方便。在TCP/IP协议中,完成该服务的协议主要是FTP协议。12.3FTP编程
12.3.1FTP简介
FTP(FileTransferProtocol,文件传输协议)是Internet上用来传送文件的协议,在Internet上通过FTP服务器可以进行文件的上传(Upload)或下载(Download)。与前面介绍的SMTP和POP3类似,FTP也是一个请求/响应协议,并且是实时联机服务,在使用它之前必须具有该服务的一个用户(用户名和口令)。工作时,客户端必须先登录到作为服务器一方的计算机上,用户登录后可以进行文件搜索和文件传送等有关操作。使用FTP可以传送所有类型的文件,如文本文件、二进制可执行文件、图像文件、声音文件及数据压缩文件等。与SMTP、POP3类似,FTP的主要操作都是基于各种命令基础之上的。常用的命令分为登录、退出登录、传输参数、文件操作命令、获得信息命令、其他命令等,表12-6给出了FTP协议主要命令,服务器应答码代表的含义可以在表12-7中查询到。表12-6FTP命令表12-7FTP服务器应答码含义相比于SMTP和HTTP,FTP协议要复杂得多。复杂的原因是FTP协议要用到两个TCP连接:一个是命令链路,用来在FTP客户端与服务器之间传递命令;另一个是数据链路,用来上传或下载数据。对于这两条链路,FTP协议有两种工作方式:PORT方式和PASV方式,即主动式和被动式。PORT(主动)式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接并建立一条命令链路。当需要传送数据时,客户端在命令链路上用PORT命令告诉服务器:
“我打开了××××端口,你过来连接我”。于是服务器从20端口向客户端的××××端口发送连接请求,建立一条数据链路来传送数据。PASV
(被动)式的连接过程是:客户端向服务器的FTP端口(默认是21)发送连接请求,服务器接受连接,建立一条命令链路。当需要传送数据时,服务器在命令链路上用PASV命令告诉客户端:“我打开了××××端口,你过来连接我”。于是客户端向服务器的××××端口发送连接请求,建立一条数据链路来传送数据。概括而言,主动模式是客户端向服务器敲门,然后客户端开门;被动模式是客户端向服务器敲门,然后服务器开门。基于FTP命令和应答码的客户端与服务器交互过程如图12-5所示。对于FTP的更多介绍,可以参阅RFC959等更多文献,为了突出网络编程多线程设计,下面的编程实习将对FTP连接过程进行简化,并只采用一个TCP连接传送命令与数据。
12.3.2WinInet类
为了能够在较高层次建立Internet客户应用程序,使编程过程得到进一步简化,MFC对WinInetAPI函数进行了封装,从而使程序员从那些复杂的端口和协议细节中解脱出来。WinInet类继承了WinInetAPI的FTP、HTTP、Gopher的功能,提供了13个对这三种服务进行访问的类:CInternetSession、CInternetConnection、CFtpConnection、CGopherConnection、CHttpConnection、CInternetFile、CGopherFile、CHttpFile、CFileFind、CFtpFileFind、CGopherFileFind、CGopherLocator、CInternetException,在afxinet.h文件中声明。这里着重介绍基于WinInet类进行FTP客户端编程时所需要使用的四个类:CInternetSession、CFtpConnection、CInternetFile、CInternetException。
1.CInternetSession类
CInternetSession类负责创建并初始化一个或几个同步Internet会话,如其成员函数GetFtpConnection()就可以创建一个CFtpConnection对象,实现打开与FTP服务的一个连接。如果需要还可以向代理服务器描述连接。图12-6CInternetSession类的派生结构
CInternetSession类提供的QueryOption()、SetOption()、OpenURL()、GetFtpConnection()、GetHttpConnection()、GetGopherConnection()、EnableStatusCallback()、EnableStatusCallback()、ServiceTypeFromHandle()等成员函数,用于完成对连接属性的查询、设置等操作。另外,还提供成员函数GetContext()、Close()、SetCookie()、GetCookie()、GetCookieLength(),实现对Cookie的查询、设置等操作。此外,它还重载了OnStatusCallback()函数,用于更新操作状态、重载函数operator(),HINTERNET用于从当前Internet会话中得到Windows句柄。12.3.1节中描述的FTP客户端与服务器端复杂的交互过程就是由成员函数GetFtpConnection()所完成的。
CInternetSession类的派生结构如图12-6所示。2.CFtpConnection类
CFtpConnection类管理到Internet服务器的FTP连接,并允许用户操作服务器上的目录和文件。CFtpConnection类无需专门创建,只需调用GetFtpConnection()成员函数得到其返回值即可。图12-7CFtpConnection类的派生结构
CFtpConnection类的操作函数有:SetCurrentDirectory()、GetCurrentDirectory()、RemoveDirectory()、GetCurrentDirectory-AsURL()、CreateDirectory()、Rename()、Remove()、PutFile()、GetFile()、OpenFile()、Close(),可以完成设置服务器上的FTP目录、得到连接的当前目录等操作。图12-8CInternetFile类派生结构
3.CInternetFile类
CInternetFile类及其派生类允许对使用Internet协议的远程系统中的文件进行操作。同样,CInternetFile类无需专门创建,在FTP编程中只需调用CFtpConnection的OpenFile()成员函数得到其返回值即可。
CInternetFile类的操作函数有SetWriteBufferSize()和SetReadBufferSize(),可以完成设置写入缓冲区的尺寸、设置读出缓冲区的尺寸等操作。CInternetFile类重载了CFile类的Seek()、Read()、Write()、Abort()、Flush()、Close()、ReadString()、WriteString()函数,使之适合于对FTP服务器上的文件进行相应操作。该类的对象也可以使用operatorHINTERNET从当前Internet会话中得到Windows句柄。CInternetFile类还有一个成员m_hFile,代表了与它的对象相关的文件句柄。
4.CInternetException类
CInternetException类代表与Internet操作相关的异常,该类包含两个公共数据成员:一个保存与异常有关的错误码,另一个保存导致错误应用程序的上下文ID。
CInternetException类用成员函数Dump()或GetErrorMessage()获得相关的错误信息。它的两个参数m_dwError、m_dwContext分别代表前面提到的错误码和上下文ID。CInternetException类派生结构如图12-9所示。
关于更多的WinInet类可以参阅MSDN。
12.3.3FTP客户端设计
FTP客户端的设计与HTTP的设计十分类似,用户要实现某些行为,必须具备先决条件(如读取文件,就必须先建立一个Internet连接)。与前一节不同的是,本节采用MFC的WinInet类的编程方法进行实现,表12-8列出了一般的FTP客户端行为要实现某个目标所必须使用的方法。
表12-8MFCWinInet类FTP客户端设计流程表12-9MFCWinInet类FTP客户端文件删除设计流程
对于网络中的设备管理员有进行网络管理信息收集与设置的需求,对于这种需求可以通过SNMP这种应用层协议的支持来实现。虽然SNMP不是直接为用户传递信息设计的,但是对于传输信息的网络管理至关重要,也是需要加以重视的一种应用协议。12.4SNMP编程
12.4.1SNMP简介
SNMP(SimpleNetworkManagementProtocol)即简单网络管理协议,它为网络管理系统提供了底层网络管理的框架。SNMP是一种无连接协议,其工作是通过使用请求报文和返回响应的方式,在管理代理和管理员之间传送信息,这种机制减轻了管理代理的负担。SNMP网络管理主要分为四部分:被管理节点(设备)、客户代理、网络管理工作站、网络管理协议。实际上,SNMP采用了C/S模型的一种特殊形式:代理/管理站模型,对网络的管理与维护是通过管理工作站与SNMP代理间的交互工作完成的,每个SNMP从代理负责回答SNMP管理工作站的各种查询了。客户端代理安装在被管理节点设备上。
1.MIB库
SNMP管理者收集网络设备信息并记录在MIB(ManagementInformationBase,管理信息库)中,这些网络设备信息包括设备特性、数据吞吐量、通信超载和错误等。MIB数据对象以一种树状分层结构进行组织,其每个分枝都有一个专用的名字和一个数字形式的标识符。基于这个树状分层结构,可以使用MIB浏览器方便而且简洁的方式访问整个MIB数据库。MIB中的对象使用Oid(对象标志符)。Oid是以SMI(StructureofManagementInformation)管理信息结构为基础的一系列点分符号,如.,这些点分符号在任何网络设备中都唯一标识某一个数据参数,例如IBM为.4.1.2},Cisco为{.4.1.9},Novell为{.4.1.23}等。这里,SMI实际上起到规范MIB内容的作用,可以理解成一种语言,它使MIB中描述的信息不存在二义性。对具体oid数据的含义可在/网站进行查询。
2.SNMP操作
SNMP协议定义了请求/设置网络管理数据的操作,可用于处理管理代理定义的各种任务。SNMP协议之所以易于使用,这是因为它对外提供了三种用于控制MIB对象的基本操作命令,分别是Set、Get和Trap,如表12-10所示。表12-10SNMP命令的含义
3.SNMP报文
SNMP提供的操作实现了网络管理员和管理代理之间的信息交换,这些信息是装载在SNMP报文数据报中的。一条SNMP报文由三部分组成:版本(version)域、分区(community)域和SNMP协议数据单元(ProtocolDataUnit,PUD)域,数据包的长度不是固定的。
版本域:用于说明现在使用的是哪个版本的SNMP协议,管理工作站与客户代理必须使用相同版本的SNMP协议。
分区域:分区是基本的安全机制,用于实现SNMP网络管理员访问SNMP管理代理时的身份验证。协议数据单元域:指明了SNMP的消息类型及相关参数。SNMPv1的PDU有五种类型,有些是报文请求(Request),有些则是响应(Response)。它们包括GetRequest、GetNextRequest、SetRequest、GetResponse、Trap。SNMPv2又增加了两种PDU类型:GetBulkRequest和InformRequest。
12.4.2SNMP的开发方法
SNMP的开发需要在被管理的设备上安装SNMP客户端代理,并使用合适的开发工具。
1.SNMP代理安装
在运用SNMP应用程序之前,需要查看目标系统中是否已经安装有SNMP客户端代理,Windows操作系统下的查看方法是:单击“开始→运行→cmd”,按“回车”键,输入命令“netstartsnmp”,如图12-10所示,表示SNMP已安装,否则需先安装SNMP客户端代理。图12-10SNMP客户端代理安装情况查询
Windows平台下安装SNMP客户端代理的方法如下:
(1)打开控制面板,双击“添加/删除程序”项。
(2)在弹出的“添加/删除程序”对话框的左窗格中,单击“添加/删除Windows组
件”项。
(3)在弹出的“Windows组件向导”中双击“管理和监视工具”项。
(4)在弹出的“管理和监视工具”对话框中勾选“简单网络管理协议”,单击“确定”按钮。安装协议时需要用到与当前操作系统相配套的系统安装盘,然后可能需要重启。
(5)再按前面执行“netstartsnmp”命令那样启动SNMP服务。
2.SNMP的开发工具
在基于SNMP的开发时,有以下几种选择:
(1)采用直接网络编程的方法直接发UDP包,这样最原始、灵活,基本上完全自主,但是开发过于麻烦。
(2)在Windows系统下可以使用Microsoft提供的WinsnmpAPI函数。
(3)选择一些第三方的源SNMP开发包,比如像NET-SNMP、SNMP++
等。
本书重点介绍基于WinsnmpAPI函数的SNMP协议编程。
12.4.3WinsnmpAPI主要函数
整个SNMP编程要经过一个创建、执行、销毁的过程,即先加载SNMP的功能,接着执行所进行的操作,最后释放占用资源,进一步细分可分为:①SNMP加载;②建立会话;③设置传输模式;④创建实体;⑤设置重传模式;⑥设置超时时间;⑦设置重传次数;⑧创建上下文句炳;⑨创建变量捆绑列表;⑩追加绑定列表;创建PDU;发送消息;接收消息、提取数据报;计算返回列表数目,取得返回结果;清理现场。以下介绍Winsnmp编程时需要使用的主要API函数。
1)加载SNMP函数
加载SNMP用到函数SnmpStartup(),原型如下:
SnmpStartup(smiLPUINT32nMajorVersion, //WinSNMPAPI主版本号
smiLPUINT32nMinorVersion, //WinSNMPAPI副本号
smiLPUINT32nLevel, //SNMP执行支持级别
smiLPUINT32nTranslateMode, //默认的实体/上下文传输模式
smiLPUINT32nRetransmitMode); //默认的重发机制
2)建立会话函数
建立SNMP会话使用函数SnmpOpen()或SnmpCreateSession(),原型如下:
HSNMP_SESSIONSnmpOpen(HWNDhWnd, //接收消息窗口句炳
UINTwMsg); //hWnd窗口需要接收的消息码
HSNMP_SESSIONSnmpCreateSession(HWNDhWnd, /接收消息窗口句炳
UINTwMsg, //hWnd窗口需要接收的消息码
SNMPAPI_CALLBACKpfnCallBack,//回调函数句柄
LPVOIDlpClientData);//回调函数数据指针
第二个函数并没有被完全确定下来,它只是为程序员在编程过程中不是基于Windows的编程提供一种选择。
3)设置传输模式函数
设置传输模式使用函数SnmpSetTranslateMode(),原型如下:
SNMPAPI_STATUSSnmpSetTranslateMode(smiUINT32nTranslateMode);
该函数只有一个参数,有以下几种选择:SNMPAPI_TRANSLATED(不常用)、SNMPAPI_UNTRANSLATED_V1(版本V1)、SNMPAPI_UNTRANSLATED_V2(版本V2),可以选择任一个参数。
4)创建实体函数
创建实体用到的函数是HSNMP_ENTITYSnmpStrToEntity(),原型如下:
HSNMP_ENTITYSnmpStrToEntity( HSNMP_SESSIONsession, //WinSNMP会话句柄
LPCSTRstring); //用于识别实体的字符串指针
该函数的第一个参数是第2步返回的会话句炳,第二个参数与第3步中设置的传输模式有关,如果你选择后两个参数,那么这里的string就是所要发送消息的网络设备IP地址或接收消息的管理设备IP地址。该函数返回一个实体句炳。
5)设置重传模式函数
设置重传模式用到的函数是SnmpSetRetransmitMode(),原型如下:
SNMPAPI_STATUSSnmpSetRetransmitMode(smiUINT32nRetransmitMode);
该函数只有一个参数,有以下两种选择:SNMPAPI_ON,标识启动重传模式;SNMPAPI_OFF,标识关闭重传模式。
6)设置超时时间函数
设置超时时间用到的函数是SnmpSetTimeout(),原型如下:
SNMPAPI_STATUSSnmpSetTimeout( HSNMP_ENTITYhEntity, //实体句柄
smiTIMETICKSnPolicyTimeout); //超时的时间
该函数的第一个参数是第4步返回的实体句炳,通常设置目标实体的超时时间,也就是接收消息的网络设备的实体。
7)设置重传次数函数
用到的函数是SnmpSetRetry(),原型如下:
SNMPAPI_STATUSSnmpSetRetry(HSNMP_ENTITYhEntity, //实体句柄
smiUINT32nPolicyRetry); //重传次数
该函数的第一个参数是第4步返回的实体句柄,通常设置目标实体的重传次数,也就是接收消息的网络设备的实体。
8)创建上下文句柄函数
创建上下文句柄用到的函数是SnmpStrToContext(),原型如下:
HSNMP_CONTEXTSnmpStrToContext(HSNMP_SESSIONsession, //会话句柄
smiLPCOCTETSstring); //字符串结构指针
该函数的第一个参数是第2步返回的会话句柄,第二个参数与设置的传输模式有关。该函数返回一个上下文句柄。由第1步、第4步、第8步得到三个重要的句柄:会话句柄、实体句柄、上下文句柄。
9)创建变量捆绑列表函数
创建变量捆绑列表用到的函数是SnmpCreateVbl(),原型如下:
HSNMP_VBLSnmpCreateVbl(HSNMP_SESSIONsession, //会话句柄
smiLPCOIDname, //指向变量名的指针
smiLPCVALUEvalue); //指向变量值的指针
10)追加绑定列表函数
追加绑定列表用到的函数是SnmpSetVb(),原型如下:
SNMPAPI_STATUSSnmpSetVb(HSNMP_VBLvbl, //绑定列表句柄参数
smiUINT32index, //变量绑定索引
smiLPCOIDname //实体变量名部分指针
smiLPCVALUEvalue); //实体变量值部分指针
11)数据发送函数
数据发送用函数SnmpCreatePdu(),原型如下:
HSNMP_PDUSnmpCreatePdu(HSNMP_SESSIONsession, //会话句柄
smiINTPDU_type, //PDUtype
smiINT32request_id, //PDU请求标识
smiINTerror_status, //错误状态
smiINTerror_index, //错误索引
HSNMP_VBLvarbindlist); //变量绑定列表句柄第二个参数很重要,它表示想要执行的操作方式,有如下的选项:SNMP_PDU_GET、SNMP_PDU_GETNEXT、SNMP_PDU_RESPONSE、SNMP_PDU_SET、SNMP_PDU_V1TRAP、SNMP_PDU_GETBULK、SNMP_PDU_TRAP。第三个参数request_id对于异步操作用来标志某一个请求的消息。error_status和error_index在SNMP_PDU_GETBULK操作中分别为PDU中的non_repeaters域和max_repetitions域指定一个值。error_status仅对SNMP_PDU_GETBULK请求有效,error_index仅对SNMP
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
评论
0/150
提交评论