WinInet简介
WinInet是微软提供的对Windows客户端与服务器通信的API,它是一个网络编程接口,包含了HTTP、FTP和Gopher,借助于这些接口,开发者不需要了解底层的一些知识,只需要简单的了解HTTP或者FTP协议即可,Gopher慢慢的已经淡出我们的视线,有兴趣的同学可以去找谷歌了解。还有一个微软提供的库WinHttp,主要用于解决服务器和服务器通信。
WinInet的使用方法
WinInet的一般使用流程:
(1). 建立连接(InternetOpen、InternetConnect)
(2). 发送请求(HttpOpenRequest 、HttpSendRequest)
(3). 读取接受的数据(InternetReadFile)
(4). 关闭连接(InternetCloseHandle)
当然,和服务器通信过程中,肯定会涉及到字符集编码转换,我最近在研究NotePad++的源码,我会整理下它的源码中的关于字符集转换的代码,防止大家重复造轮子。
Http几个重要的通信方式
1. GET 从服务器获取URL对应的资源。
2. POST 用于修改、注解URL对应的资源。
3. PUT 修改或者新建URL对应的资源。
4. DELETE 删除URL对应的资源。
HINTERNET HttpOpenRequest( _In_ HINTERNET hConnect, _In_ LPCTSTR lpszVerb, _In_ LPCTSTR lpszObjectName, _In_ LPCTSTR lpszVersion, _In_ LPCTSTR lpszReferer, _In_ LPCTSTR *lplpszAcceptTypes, _In_ DWORD dwFlags, _In_ DWORD_PTR dwContext );
HttpOpenRequest的第2个参数 lpszVerb 就是用来指定Http通信方式的,例如:需要指定POST方式,lpszVerb为_T("POST")。
WinInet编程具体例子
1.获取www.baidu.com的网页内容
// TODO: Add extra validation here
HINTERNET hssetion;
HINTERNET hConnect;
HINTERNET hRequrest;
hssetion = InternetOpen( "httpDownload", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0 );
if ( NULL == hssetion )
{
::AfxMessageBox("open failed");
return;
}
hConnect = InternetConnect( hssetion, "www.baidu.com", INTERNET_DEFAULT_HTTP_PORT,
NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0 );
if ( NULL == hConnect )
{
::AfxMessageBox("connect failed");
return;
}
const char* pFileType = "*/*";
hRequrest = HttpOpenRequest( hConnect, "GET", "/index.html", "HTTP/1.1", NULL,
&pFileType, INTERNET_FLAG_RELOAD, 0 );
if ( NULL == hRequrest )
{
::AfxMessageBox("open requrest failed");
return;
}
BOOL bBet = HttpSendRequest( hRequrest, NULL, 0, NULL, 0 );
if ( !bBet )
{
::AfxMessageBox("send request failed");
return;
}
DWORD BufLen=4*1024*10;
char buffer[4*1024*10] = {0};
DWORD infoLen=4*1024*10;
DWORD dwIndex=0;
bool RetQueryInfo=HttpQueryInfo(hRequrest,
HTTP_QUERY_CONTENT_LENGTH,
buffer, &infoLen,
&dwIndex);
if ( !RetQueryInfo)
{
::AfxMessageBox("query info failed");
return;
}
int FileSize = atoi(buffer); // 文件大小
DWORD dwRequest; // 请求下载的字节数
DWORD dwRead; // 实际读出的字节数
dwRequest=BufLen;
FILE* f = fopen( "Download", "ab" );
while(true)
{
memset( buffer, 0, BufLen );
bool ReadReturn = InternetReadFile(hRequrest,
(LPVOID)buffer,
dwRequest,
&dwRead);
if(!ReadReturn)break;
if(dwRead==0)break;
// 保存数据
// buffer[dwRead]='\0';
// FileWrite(iFileHandle, buffer, dwRead);
fwrite( buffer, 1, dwRead, f );
// dwCount = dwCount + dwRead;
// 发出下载进程事件
}
2.提交请求数据
/** POST 参数*/
struct PostParam
{
string strResponse; // 请求反馈
TCHAR* szObjName; // 请求资源
char* pszPostData; // post 数据
};
/**
* @brief 向提交数据
* @param[in] pServerAddr 服务器地址
* @param[in] nPort 端口
* @param[in/out] pParam 服务器提交数据
* @return -1 请求失败
* @return 0 成功
* @date 2013-08-27 10:09:35
*/
int HttpPost(TCHAR* pServerAddr, INTERNET_PORT nPort,PostParam& pParam)
{
DWORD dwServiceType = INTERNET_SERVICE_HTTP;
/*wchar_t* szHeaders = L"Content-Type: application/x-www-form-urlencodedn";*/
// 这个就看服务器怎么定义的
wchar_t* szHeaders = L"Content-Type: application/json";
HINTERNET hOpen = NULL;
HINTERNET hConnect = NULL;
HINTERNET hRequest = NULL;
hOpen = InternetOpen(TEXT("PervasiveBiz"), INTERNET_OPEN_TYPE_DIRECT,NULL, 0, 0);
if (!hOpen)
{
return -1;
}
// 创建连接
if (!(hConnect = InternetConnect(hOpen, pServerAddr, nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0)))
{
InternetCloseHandle(hOpen);
return -1;
}
LPTSTR AcceptTypes[2] = {TEXT("Accept: */*"), NULL};
DWORD dwFlags = INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE;
// Open an HTTP request handle.
if (!(hRequest = HttpOpenRequest (hConnect, TEXT("POST"), pParam.szObjName, HTTP_VERSION, NULL,NULL /*(LPCTSTR*)AcceptTypes*/, dwFlags, 0)))
{
InternetCloseHandle(hConnect);
InternetCloseHandle(hOpen);
return -1;
}
if(!HttpAddRequestHeaders(hRequest, szHeaders, -1, HTTP_ADDREQ_FLAG_ADD|HTTP_ADDREQ_FLAG_REPLACE))
{
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hOpen);
return -1;
}
if(HttpSendRequest(hRequest, NULL, -1,pParam.pszPostData , strlen(pParam.pszPostData)) == FALSE)
{
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hOpen);
return -1;
}
DWORD dwSize = 0;
int dwHttpStatus = -1;
DWORD index = 0;
LPVOID lpOutBuffer = NULL;
while (!HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, (LPVOID)lpOutBuffer, &dwSize, NULL))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_INSUFFICIENT_BUFFER)
{
lpOutBuffer = new wchar_t[dwSize];
}
else
{
fprintf(stderr, "HttpQueryInfo failed, error = %d (0x%x)\n",
GetLastError(), GetLastError());
break;
}
}
dwHttpStatus = _wtoi((wchar_t*)lpOutBuffer);
delete lpOutBuffer;
lpOutBuffer = NULL;
if ((dwHttpStatus<200)||(dwHttpStatus>300))
{
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hOpen);
return -1;
}
char buffer[1024] = {0};
do
{
memset(buffer, 0, 1024);
if(!InternetReadFile (hRequest, (LPVOID)(buffer),1023, &dwSize))
{
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hOpen);
return -1;
}
if(dwSize > 0)
{
buffer[dwSize] = 0;
(pParam.strResponse) += buffer;
}
}while (dwSize != 0);
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hOpen);
return 0 ;
}
WinInet的bug
1 . 设置各种超时时间不起作用。
这个bug微软也承认了,但是现在还没有修复,不晓得微软在搞什么鬼。
http://support.microsoft.com/kb/q176420
2. 有可能会引起crash
这个bug也比较常见,在网上可以搜出很多相关网页。