1 MFC 处理 HTTP 请求的基本方法
1.1 配置本地的 HTTP 服务器
为方便测试,可以先配置一个本地的 HTTP 服务器,根据各种需要进行定制。
我在这里,用 JSP 定制了一个基本的 HTML 表单程序,分为 index.jsp 和 RequestObjectInJSP.jsp 两个文件。其中,index.jsp 用来提供表单程序,方便测试 RequestObjectInJSP.jsp 这个表单处理文件。
为了减少在测试时期网络通信的影响,强烈建议搭建一个本地的 Web 服务器。
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,从而保证收到的消息一样。