VC连接HTTP服务器 & MFC 处理 HTTP 请求的基本方法

10 篇文章 0 订阅

1.AfxParseURL函数,该函数解析URL字符串并返回服务的类型及组件,包含在 afxinet.h 头文件中。
 BOOL AFXAPI AfxParseURL(LPCTSTR pstrURL,DWORD& dwServiceType,CString& strServer,CString& strObject,INTERNET_PORT& nPort);   
   pstrURL : 一个字符串指针,指向要解析的URL。   
   dwServiceType:指示互联网服务的类型。
     可以取下列值:   AFX_INET_SERVICE_FTP   
                        AFX_INET_SERVICE_HTTP   
                        AFX_INET_SERVICE_HTTPS   
                        AFX_INET_SERVICE_GOPHER   
                        AFX_INET_SERVICE_FILE   
                        AFX_INET_SERVICE_MAILTO   
                        AFX_INET_SERVICE_NEWS   
                        AFX_INET_SERVICE_NNTP   
                        AFX_INET_SERVICE_TELNET   
                        AFX_INET_SERVICE_WAIS   
                        AFX_INET_SERVICE_MID   
                        AFX_INET_SERVICE_CID   
                        AFX_INET_SERVICE_PROSPERO   
                        AFX_INET_SERVICE_AFS   
                        AFX_INET_SERVICE_UNK   
   strServer :接收待解析的URL服务器名,服务类型后的第一个部分。   
   strObject: 接收待解析的URL 涉及的对象(可能为空)。   
   nPort: 如果存在,则从URL的服务器或对象部分搜索出来

2.CInternetSession类 继承自CObject,使用类CInternetSession 创建并初始化一个或多个同时的Internet 会话。如果需要,还可描述与代理服务器的连接。如果Internet连接必须在应用过程中保持着,可创建一个类CWinApp的CInternetSession成员。一旦已建立起Internet 会话,就可调用OpenURL。CInternetSession会通过调用全局函数AfxParseURL来为分析映射URL。无论协议类型如何,CInternetSession 解释URL并管理它。它可处理由URL资源“file://”标志的本地文件的请求。如果传给它的名字是本地文件,OpenURL 将返回一个指向CStdioFile对象的指针。   如果使用OpenURL在Internet服务器上打开一个URL,你可从此处读取信息。如果要执行定位在服务器上的指定的服务(例如,HTTP,FTP或Gopher)行为,必须与此服务器建立适当的连接。直接打开与指定的服务器的指定的类型的连接,请使用下列成员函数:
 GetGopherConnection 打开与Gopher服务的连接。 
  GetHttpConnection 打开与HTTP服务的连接。 
  GetFtpConnection 打开与FTP服务的连接。 
 QueryOption和SetOption允许设置会话的查询选项,如超时值、再试次数等等。 
 Internet会话过程中,象查找或数据下载这样的事务处理会占用一定的时间。使用者可能想继续工作,或获得事务处理进程的状态信息。为解决这个问题,CInternetSession可以让查找和数据传输异步发生,允许使用者在传输结束时进行其它任务。如果要为使用者提供状态信息,或异步处理任意操作,必须设置三个条件:
   ①.在构造函数中,dwFlags必须包括INTERNET_FLAG_ASYNC。 
    ②.在构造函数中,dwContext必须设置为1。 
    ③.必须通过调用EnableStatusCallback来建立回调函数。 
 使用覆盖成员函数OnStatusCallback来获得异步获取的状态信息。使用此覆盖成员函数,必须从CInternetSession派生你自己的类。
 注意:   CInternetSession将为不支持的服务类型产生一个AfxThrowNotSupportedException。当前只支持下列服务类型:FTP,HTTP,Gopher和文件。所在头文件#include <afxinet.h>
 CInternetSession类成员:
 QueryOption 为错误检查提供可能的断言 
 SetOption 为Internet会话设置选项 
 OpenURL 文法分析映射并打开一个URL 
 GetFtpConnection 打开一个与服务器的FTP会话。写入用户日志 
 GetHttpConnection 为试图打开连接的应用打开一个HTTP服务器 
 GetGopherConnection 为试图打开连接的应用打开一个Gopher服务器 
 EnableStatusCallback 建立一个状态回调例程。异步操作需要EnableStatusCallback 
 ServiceTypeFromHandle 从Internet句柄中得到服务器类型 
 GetContext 为Internet或应用会话获得上下文的值 
 Close 当Internet会话终止时关闭Internet连接 
 SetCookie 为指定的URL设置小程序 
 GetCookie 返回指定的URL的小程序及其所有父URL 
 GetCookieLength 获取确定存储在缓冲区的小程序的长度的变量

 可覆盖的函数OnStatusCallback 当状态回调有效时,更新操作状态 
 操作符operator HINTERNET 当前Internet会话的句柄

3.CHttpConnection类,MFC类CHttpConnection管理与HTTP服务器的连接。HTTP是用MFCWinInet类实现的三个Internet服务器协议之中的一个。   类CHttpConnection包含一个构造函数和一个成员函数OpenRequest,使用HTTP协议来管理与服务器的连接。   要与一个HTTP服务器通讯,必须先构造一个CInternetSession的实例,然后构造一个CHttpConnection对象。不能直接构造一个CHttpConnection对象,而是调用CInternetSession::GetHttpConnection,创建CHttpConnection对象并返回其指针。 
4.CFtpConnection类,MFC类FtpConnection管理与Internet服务器的FTP连接并允许直接操纵服务器中的目录和文件。FTP是由MFC WinInet类识别的三种Internet服务器之一。   为了与FTP Internet服务器通讯,必须先创建一个CInternetSession实例,然后创建CFtpConnection对象。创建CFtpConnection对象不采用直接方式,而是调用CInternetSession::GetFtpConnertion来创建并返回一个指向它的指针。 
 成员函数:
 SetCurrentDirectory 设置当前FTP目录 
 GetCurrentDirectory 获取此次连接的当前目录 
  GetCurrentDirectoryAsURL 获取作为URL的此次连接的当前目录 
  RemoveDirectory 从服务器移去指定目录 
  CreateDirectory 在服务器上构造一个目录 
  Rename 将服务器上的文件改名 
  Remove 从服务器上移去一个文件 
  PutFile 将一个文件放到服务器上 
  GetFile 从连接的服务器上获取一个文件 
  OpenFile 在连接的服务器上打开一个文件 
  Close 关闭与服务器的连接 
5.CGopherConnection类,MFC类的CGopherConnection管理与Gopher Internet服务器的连接。Gopher服务器是由MFC WinInet类识别的三种 Internet服务器之一。CGopherConnection包含一个构造函数和三个附加成员函数,用于管理Gopher服务器:OpenFile,CreateLocator和GetAttribute。   为与一个Gopher Internet服务器相连接,必须首先建立一个CInternetSession实例,然后调用CInternetSession::GetGophConnection来构造一个CGopherConnection对象并返回一个指向它的指针。不可直接创建一个CGopherConnection对象。所在头文件#include <afxinet.h>


CString GetHttpFileForProxy(const char *url,const char*ProxyServerIPAndPort, const char *ProxyUserName ,const char *strProxyPassWD) 

 CString strContent;
 char strProxyList[MAX_PATH], strUsername[64], strPassword[64]; 
 //in this case "proxyserver" is the proxy server name, "8080" is its port 
 //ProxyList输入的格式为ProxyServerName:ProxyServerPort 210.45.242.8:8080
 strcpy(strProxyList, strProxyServerIPAndPort); //代理服务器和端口
 strcpy(strUsername, strProxyUserName); //代理用户名
 strcpy(strPassword, strProxyPassWD); //代理用户名对应密码
 DWORD dwServiceType = AFX_INET_SERVICE_HTTP; 
 CString strServer, strObject; 
 INTERNET_PORT nPort; 
 AfxParseURL(url, dwServiceType, strServer, strObject, nPort); 
 CInternetSession mysession; 
 CHttpConnection* pConnection; 
 CHttpFile* pHttpFile; 
 pConnection = mysession.GetHttpConnection(strServer,INTERNET_FLAG_KEEP_CONNECTION,INTERNET_INVALID_PORT_NUMBER,NULL, NULL); 
 pHttpFile = pConnection->OpenRequest("GET", strObject,NULL, 0, NULL, NULL,INTERNET_FLAG_KEEP_CONNECTION); 
 //here for proxy //这里设置代理服务器和端口、用户名和密码
 INTERNET_PROXY_INFO proxyinfo; 
 proxyinfo.dwAccessType = INTERNET_OPEN_TYPE_PROXY; 
 proxyinfo.lpszProxy = strProxyList; 
 proxyinfo.lpszProxyBypass = NULL; 
 mysession.SetOption(INTERNET_OPTION_PROXY, (LPVOID)&proxyinfo, sizeof(INTERNET_PROXY_INFO)); 
 pHttpFile->SetOption(INTERNET_OPTION_PROXY_USERNAME, strUsername, strlen(strUsername)+1); 
 pHttpFile->SetOption(INTERNET_OPTION_PROXY_PASSWORD, strPassword, strlen(strPassword)+1);

 pHttpFile->SendRequest(NULL); 
 
 //输出string信息 分行放到m_SiteInfo
 CString myData;
 while(pHttpFile->ReadString(myData))
 {
    strContent=strContent+"\r\n";
    strContent+=myData;
 }

 pHttpFile->Close(); 
 delete pHttpFile; 
 pConnection->Close(); 
 delete pConnection; 
 mysession.Close();
 return strContent;
}




1 MFC 处理 HTTP 请求的基本方法

1.1 配置本地的 HTTP 服务器

为方便测试,可以先配置一个本地的 HTTP 服务器,根据各种需要进行定制。

我在这里,用 JSP 定制了一个基本的 HTML 表单程序,分为 index.jsp 和 RequestObjectInJSP.jsp 两个文件。其中,index.jsp 用来提供表单程序,方便测试 RequestObjectInJSP.jsp 这个表单处理文件。

为了减少在测试时期网络通信的影响,强烈建议搭建一个本地的 Web 服务器。

1.2 MFC 发起 HTTP 请求的基本方法

用 CInternetSession 来发起 Http 请求,需要包含头文件:

#include <afxinet.h>

MFC 发起 HTTP 请求的逻辑,和用 WinINet 函数集 的整体过程类似,主要的步骤在 Steps in a Typical HTTP Client Application 有详细的描述。

Retrieving a file via. HTTP 一文也对 MFC 发起 HTTP 请求有着非常详细的介绍。

1.3 用 MFC 发起 HTTP GET 请求

Get 服务类别,估计是 HTML 里最常用的,平时浏览网页用的就是这种。下面是用 GET 的方法来请求某个网页的内容,代码如下:

//通过 http GET 协议来获取并保存文件
CInternetSession session;
session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, 1000 * 20);
session.SetOption(INTERNET_OPTION_CONNECT_BACKOFF, 1000);
session.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 1);

CHttpConnection* pConnection = session.GetHttpConnection(TEXT("localhost"),(INTERNET_PORT)8080);
CHttpFile* pFile = pConnection->OpenRequest( CHttpConnection::HTTP_VERB_GET,
                                             TEXT("/Practice/index.jsp"));

CString szHeaders = L"Accept: audio/x-aiff, audio/basic, audio/midi,\
                     audio/mpeg, audio/wav, image/jpeg, image/gif, image/jpg, image/png,\
                     image/mng, image/bmp, text/plain, text/html, text/htm\r\n";

pFile->AddRequestHeaders(szHeaders);

pFile->SendRequest();

DWORD dwRet;
pFile->QueryInfoStatusCode(dwRet);

if(dwRet != HTTP_STATUS_OK)
{
    CString errText;
    errText.Format(L"POST出错,错误码:%d", dwRet);
    AfxMessageBox(errText);
}
else
{
    int len = pFile->GetLength();
    char buf[2000];
    int numread;
    CString filepath;
    CString strFile = L"response.txt";
    filepath.Format(L".\\%s", strFile);
    CFile myfile( filepath,
                  CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
    while ((numread = pFile->Read(buf,sizeof(buf)-1)) > 0)
    {
        buf[numread] = '\0';
        strFile += buf;
        myfile.Write(buf, numread);
    }
    myfile.Close();
}

session.Close();
pFile->Close(); 
delete pFile;

调试上面这段代码的时候,特别要注意以下几点:

  1. CHttpConnection::GetHttpConnection() 里第一参数,填写的应该是类似 www.yahoo.com 这样的根域名,如果带上 http:// 或是子路径,好像均会出错。
  2. CHttpFile::OpenRequest() 的第一个和第二个参数很重要,会影响是否能连接,尤其是第二个参数,要输入正确的 URI 路径;
  3. 在 CHttpFile::SendRequest() 之后,一定要用 CHttpFile::QueryInfoStatusCode() 来获得请求的状态码,从而判断是否正确获得了 http 数据;

    Http 的状态码主要有以下几类:

    Group     Meaning
    200-299   Success
    300-399   Information
    400-499   Request error
    500-599   Server error
    

    更详细的代码参数:

    Status code         Meaning
      200           URL located, transmission follows
      400           Unintelligible request
      404           Requested URL not found
      405           Server does not support requested method
      500           Unknown server error
      503           Server capacity reached
    

1.4 用 MFC 发起 HTTP Post 请求

用 MFC 发起 HTTP Post 请求,主要流程和 MFC HTTP Get 代码一样,以下是示例代码:

//通过 http POST 协议来发送命令给服务器
CInternetSession session;
session.SetOption(INTERNET_OPTION_CONNECT_TIMEOUT, 1000 * 20);
session.SetOption(INTERNET_OPTION_CONNECT_BACKOFF, 1000);
session.SetOption(INTERNET_OPTION_CONNECT_RETRIES, 1);

CHttpConnection* pConnection = session.GetHttpConnection( TEXT("localhost"),
                                                          (INTERNET_PORT)8080);
CHttpFile* pFile = pConnection->OpenRequest( CHttpConnection::HTTP_VERB_POST,
                                             TEXT("/Practice/RequestObjectInJSP.jsp"),
                                             NULL,
                                             1,
                                             NULL,
                                             TEXT("HTTP/1.1"),
                                             INTERNET_FLAG_RELOAD);

//需要提交的数据
CString szHeaders   = L"Content-Type: application/x-www-form-urlencoded;";

//下面这段编码,则是可以让服务器正常处理
CHAR* strFormData = "username=WaterLin&password=TestPost";
pFile->SendRequest( szHeaders,
                    szHeaders.GetLength(),
                    (LPVOID)strFormData,
                    strlen(strFormData));

DWORD dwRet;
pFile->QueryInfoStatusCode(dwRet);

if(dwRet != HTTP_STATUS_OK)
{
    CString errText;
    errText.Format(L"POST出错,错误码:%d", dwRet);
    AfxMessageBox(errText);
}
else
{
    int len = pFile->GetLength();
    char buf[2000];
    int numread;
    CString filepath;
    CString strFile = L"result.html";
    filepath.Format(L".\\%s", strFile);
    CFile myfile(filepath,
        CFile::modeCreate|CFile::modeWrite|CFile::typeBinary);
    while ((numread = pFile->Read(buf,sizeof(buf)-1)) > 0)
    {
        buf[numread] = '\0';
        strFile += buf;
        myfile.Write(buf, numread);
    }
    myfile.Close();
}

session.Close();
pFile->Close(); 
delete pFile;

以上的代码,与 Get 对比起来,唯一的不同在于,提交 CHttpFile::SendRequest() 数据的时候,把表单的数据也带上了。



2 疑难杂症

2.1 字符编码,可恨的字符编码

对于 C/C++ 程序来说,最可恨的事情之一,莫过于字符集的问题了,尤其是在网络通信的时候,这一问题就显得更加让人恶心了。

如果在用 MFC 发起 HTTP Post 请求时,你用的是宽字符集的编码,比如说,我把用 MFC 发起 HTTP Post 请求里同样的几行代码,替换成下面这几句:

CString szHeaders   = L"Content-Type: application/x-www-form-urlencoded;charset=UTF-8";

//下面这句,因为字符集的原因,是无法让服务器正常处理
CString strFormData = L"username=WaterLin&password=TestPost";
pFile->SendRequest( szHeaders,
                    szHeaders.GetLength(),
                    (LPVOID)(LPCTSTR)strFormData,
                    lstrlen(strFormData));

如果是,在服务器端会解析为如下这样:

<br>Parameters:u s e r n a m e

当你用文本编辑器打开返回的文件时,会显示如下的错误提示:

./images/CopyError-waterlin.png

这个时候,虽然上面的 JSP 代码会输出

Character Encoding: null 

这样的值,但是服务器却会把表单内容当成 ISO-8859-1 字符集来处理,从而把表单参数解析为类似下面的怪胎:

看,这就是把字符集弄混了的下场!

则需要在 HTTP 报头里,一定要显式加上 charset=UTF-8 这样的约束,比如,在上面的代码,我就是直接这样写的:

CString szHeaders   = L"Content-Type: application/x-www-form-urlencoded;charset=UTF-8";

这样,服务器在收到你的报文时,就知道,你的 Form 表单内容,是用 UTF-8 来编码的,它也会用 UTF-8 字符集来解码你的 request,从而保证收到的消息一样。



3 用 MFC 来发起其它网络请求

3.1 用 MFC 来发起 FTP 请求

用 MFC 来发起 FTP 请求也非常方便,有兴趣的话,可以读一读这篇专门的文章



  • 2
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MFC中,连接SQLite3需要使用CDatabase类来封装连接和操作数据库的功能。 以下是使用MFC封装类连接SQLite3的方法: 1. 首先,需要确保SQLite3的库文件已经正确地引入到项目中。可以将sqlite3.dll文件复制到项目的Debug或Release文件夹中,并在项目属性的配置属性->常规->附加库目录中添加库文件所在的文件夹路径。 2. 在需要连接SQLite3数据库的地方,创建一个CDatabase对象,并调用OpenEx函数来打开数据库连接。示例代码如下: ``` CDatabase db; if (db.OpenEx(L"DRIVER=SQLite3 ODBC Driver;Database=mydatabase.db;Trusted_Connection=yes;") == TRUE) { // 数据库连接成功 } else { // 数据库连接失败 } ``` 在上面的代码中,根据SQLite3的ODBC驱动程序名称和要连接的数据库文件路径创建了一个合适的连接字符串,并传递给OpenEx函数进行连接。如果连接成功,返回值为TRUE;否则,返回值为FALSE。 需要注意的是,要确保数据库文件mydatabase.db存在于指定的路径中,否则连接将失败。 3. 连接成功后,可以通过CRecordset类来执行SQL语句并处理结果。示例代码如下: ``` CRecordset recset(&db); CString strSQL = L"SELECT * FROM mytable"; if (recset.Open(CRecordset::forwardOnly, strSQL) == TRUE) { // SQL语句执行成功,可以获取和处理结果 while (!recset.IsEOF()) { // 处理每一行数据 // ... // 移动到下一行 recset.MoveNext(); } recset.Close(); } else { // SQL语句执行失败 } ``` 在上面的代码中,通过一个SELECT语句查询指定的表mytable的所有数据,并逐行处理结果。 以上就是使用MFC封装类连接SQLite3的大致步骤和示例代码。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值