MFC POST和Get
1.2、MFC发起HHTTP请求的基本发方法
用 CInternetSession 来发起 Http 请求,需要包含头文件:
#include <afxinet.h>
MFC 发起 HTTP 请求的逻辑,和用 WinINet 函数集 的整体过程类似,主要的步骤在 Steps in a Typical HTTP Client Application 有详细的描述。
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;
调试上面这段代码的时候,特别要注意以下几点:
- CHttpConnection::GetHttpConnection() 里第一参数,填写的应该是类似 www.yahoo.com 这样的根域名,如果带上 http:// 或是子路径,好像均会出错。
- CHttpFile::OpenRequest() 的第一个和第二个参数很重要,会影响是否能连接,尤其是第二个参数,要输入正确的 URI 路径;
- 在 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
当你用文本编辑器打开返回的文件时,会显示如下的错误提示:
这个时候,虽然上面的 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,从而保证收到的消息一样。