原文地址:http://blog.csdn.net/li_guotao/archive/2009/03/04/3956067.aspx
一 WinINet是干什么的?
二 如果完成一個基本的WinINet操作流程
三 如何異步完成,異步操作的好處。
四 unicode以及mutilbyte
五 代碼樣例
一 WinINet是微軟開發(fā)的一個庫,可以完成http ftp客戶端的工作。讓程序員從復(fù)雜的協(xié)議中節(jié)省大量體力。
二 我會用了http, ftp類似。用WinINet完成一個http下載需要以下步驟 。 下面這個是同步操作(也就是一步步操作,每個函數(shù)執(zhí)行完才會返回一個結(jié)果的意思)
1 InternetOpen Initializes an application's use of the WinINet functions.
需要的話 InternetSetOption 設(shè)置代理服務(wù)器地址以及端口。
http: ip:port 或者 http=http://ip:port
socks:SOCKS=ip:port
2 InternetConnect 關(guān)聯(lián)目標地址或者域名以及服務(wù)ip
3 HttpOpenRequest 關(guān)聯(lián)要下載的內(nèi)容名字
InternetSetOption 設(shè)置用戶名密碼
4 HttpSendRequest 這步就是用HttpOpenRequest 的返回值(已經(jīng)關(guān)聯(lián)了上面的所有信息)發(fā)送出去,第一次用了網(wǎng)絡(luò)。向目標服務(wù)器或者代理服務(wù)器。
5 HttpQueryInfo
該函數(shù)查詢返回值,不參與網(wǎng)絡(luò)操作。可以查詢服務(wù)器的返回信息,比如目標文件的大小,該文件是否存在,代理服務(wù)返回了要求用戶名,密碼等等(這幾個最常用),還有很多信息。
6 InternetReadFile
很普通的讀函數(shù),就是下載文件。不知道是否和底層網(wǎng)絡(luò)同步,底層會不會提前下載呢?
7 InternetCloseHandle 釋放資源
三 異步操作,比較復(fù)雜的。 為什么需要異步操作呢? 因為涉及到網(wǎng)絡(luò)操作,某些函數(shù)在操作中可能需要時間,如果一直不返回(比如1秒)時,這時主線程要結(jié)束程序,豈不就出現(xiàn)意想不到的結(jié)果了,但是如果每個函數(shù)都能夠瞬間返回,然后通過WaitForMultipleObjects或者WaitForSingleObject等待結(jié)果的出現(xiàn)(此時就不會操作那個消耗1秒的函數(shù)了,而這個1秒函數(shù)正是要用到系統(tǒng)資源HINTERNET的)。
異步操作的目的上如,原理呢? 其實原理就是注冊一個函數(shù),在這里叫InternetStatusCallback,因為微軟寫得底層代碼要用到,所以必須格式統(tǒng)一。一旦有結(jié)果來了就通過事件通知我們,WaitForSingleObject函數(shù)就可以走了。比如等到了HINTERNET創(chuàng)建或者命令發(fā)送成功等結(jié)果。然后我們就可以第一時間安全的使用了。
API 函數(shù)如果名字最后可以帶EX,那么帶ex的就是異步操作的。
四 一定要注意,凡是有unicode和mutilbyte函數(shù)的一定要統(tǒng)一,最好都用mutilbyte的。
五: 代碼如下:
// crt_assert.c
// compile with: /c
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <iostream>
#include "winsock2.h"
#include <string>
#include <Wininet.h>
#include <windows.h>
#include <fstream> //要使用文件輸入輸出流必須的頭文件
using namespace std;
#define __HTTP_VERB_GET "GET"
#define __HTTP_VERB_POST "POST"
#define __HTTP_ACCEPT_TYPE "*/*"
#define __HTTP_ACCEPT "Accept: */*\r\n"
#define __SIZE_HTTP_BUFFER 100000
#define __SIZE_HTTP_RESPONSE_BUFFER 100000
#define __SIZE_HTTP_HEAD_LINE 2048
void CALLBACK InternetStatusCallback(
HINTERNET hInternet,
DWORD dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength);
HANDLE hEvent[3];
HINTERNET hFile;
HINTERNET hNet;
HINTERNET hSession,hConnect,hRequest;
int WaitExitEvent()
{
//return 1;
DWORD dwRet = ::WaitForMultipleObjects(3, hEvent, FALSE, 30000);//INFINITE);
int x=-1;
switch (dwRet)
{
//句柄被創(chuàng)建事件或者讀數(shù)據(jù)請求成功完成事件
case WAIT_OBJECT_0:
x=0;
cout<<"WAIT_OBJECT_0"<<endl;
//句柄被關(guān)閉事件
break;
case WAIT_OBJECT_0+1:
x=1;
cout<<"WAIT_OBJECT_1"<<endl;
//用戶要求終止子線程事件或者發(fā)生錯誤事件
break;
case WAIT_OBJECT_0+2:
x=2;
cout<<"WAIT_OBJECT_2"<<endl;
break;
default:
cout<<"WaitForMultipleObjects time out"<<endl;
return -1;
}
return x;
}
// 支持代理設(shè)置, 是否異步設(shè)置; 采用事件驅(qū)動
void WinINet3(bool setProxy, bool ASYNC)
{
hSession=NULL;
hConnect=NULL;
hRequest=NULL;
for (int i = 0; i < 3; i++)
{
hEvent[i] = CreateEvent(
NULL, // default security attributes
FALSE, // auto-reset event object
FALSE, // initial state is nonsignaled
NULL); // unnamed object
if (hEvent[i] == NULL)
{
printf("CreateEvent error: %d\n", GetLastError() );
ExitProcess(0);
}
}
char *url = " char *pip = "down.360safe.com";
char *paim = "/setup.exe";
// step 1
if(ASYNC) cout<<"異步模式"<<endl;
// setProxy =false;
if(setProxy)
{
cout<<"代理模式"<<endl;
if(ASYNC)
hSession = InternetOpen("name",
INTERNET_OPEN_TYPE_DIRECT,//|INTERNET_OPEN_TYPE_PROXY,// INTERNET_OPEN_TYPE_PROXY,
NULL,NULL,INTERNET_FLAG_ASYNC); // 異步
else
hSession = InternetOpen("name",INTERNET_OPEN_TYPE_PROXY,NULL,NULL,0); // 同步
}
else
{
if(ASYNC)
hSession = InternetOpen("name",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,INTERNET_FLAG_ASYNC); // 異步
else
hSession = InternetOpen("name",INTERNET_OPEN_TYPE_PRECONFIG,NULL,NULL,0); // 同步
}
if(!hSession){
DWORD er = ::GetLastError();
cout<<"InternetOpen error"<<endl;//, "Err", MB_OK);
return;
}
if(ASYNC)
{
//Sleep(500);
INTERNET_STATUS_CALLBACK res = ::InternetSetStatusCallback(hSession,InternetStatusCallback);
if(res == INTERNET_INVALID_STATUS_CALLBACK)
{
cout<<"InternetSetStatusCallback failed, so return "<<endl;
return ;
}
else
{
cout<<"InternetSetStatusCallback succeed, so go on "<<endl;
}
//Sleep(500);
}
char strProxyList[MAX_PATH], strUsername[64], strPassword[64];
strcpy(strProxyList, "SOCKS=58.56.87.2:1080"); // 寫上socks怎么就無效了呢???SOCKS5=172.18.132.27:1080
strcpy(strUsername, "user01");
strcpy(strPassword, "baidu");
INTERNET_PROXY_INFO proxy;
proxy.dwAccessType = INTERNET_OPEN_TYPE_PROXY;
proxy.lpszProxy = strProxyList;
proxy.lpszProxyBypass = NULL;
if( setProxy &&!InternetSetOption(hSession,INTERNET_OPTION_PROXY ,&proxy,sizeof(INTERNET_PROXY_INFO)))
{
cout<<"InternetSetOption failed"<<endl;
return ;
}
// step 2
//如果明確知道需要認證,第4,5個參數(shù)可以輸入用戶名,密碼"administrator","password"
//第2,3個參數(shù)為目標主機IP、端口號(不是代理服務(wù)器的參數(shù))
hConnect = InternetConnect(hSession,pip,INTERNET_DEFAULT_HTTP_PORT,NULL,NULL,INTERNET_SERVICE_HTTP,INTERNET_FLAG_RELOAD,0);
if(!ASYNC &&!hConnect){
cout<<"同步,InternetConnect error"<<endl;//, "Err", MB_OK);
return;
}
if( ASYNC&& hConnect== NULL)// 異步 需要等待 竟然直接創(chuàng)建好了
{
int er = GetLastError();
DWORD dwError = ::GetLastError();
if (dwError != ERROR_IO_PENDING)
{
cout<<"CHttpDownload::OpenInternetConnection| 連接失敗" <<endl;
return ;
}
else //
{
cout<<"hConnect == NULL, so run WaitExitEvent"<<endl;
WaitExitEvent(); // 等待成功創(chuàng)建 // 這里應(yīng)該等待 這里應(yīng)該顯示一次呀
::ResetEvent(hEvent[0]);
::ResetEvent(hEvent[1]);
::ResetEvent(hEvent[2]);
}
}
cout<<"step 2 :InternetConnect secced"<<endl;
// ::InternetSetStatusCallback(hConnect,InternetStatusCallback);
// step 3!!!
char szHead[] = "Accept: */*\r\n\r\n";
char **p = new char*[2];*p = szHead;*(p+1) = NULL;
//hRequest = HttpOpenRequest(hConnect,"GET","download/BaiduHi_1.0_Beta2.exe",NULL,NULL,/*(const char **)p*/NULL,0/*INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_RELOAD*/,0); // no request;
CONST TCHAR *szAcceptType=__HTTP_ACCEPT_TYPE;
hRequest = ::HttpOpenRequest(hConnect,
"GET",
paim,
HTTP_VERSION,
"",
&szAcceptType,
INTERNET_FLAG_RELOAD|INTERNET_FLAG_KEEP_CONNECTION|INTERNET_FLAG_NO_CACHE_WRITE,
0);
//::HttpAddRequestHeaders( hRequest, __HTTP_ACCEPT, strlen(__HTTP_ACCEPT), HTTP_ADDREQ_FLAG_REPLACE);
/*_hHTTPRequest=::HttpOpenRequest( _hHTTPConnection,
__HTTP_VERB_GET, // HTTP Verb
szURI, // Object Name
HTTP_VERSION, // Version
"", // Reference
&szAcceptType, // Accept Type
INTERNET_FLAG_KEEP_CONNECTION | INTERNET_FLAG_NO_CACHE_WRITE,
0); // context call-back point
*/
if (!ASYNC&& !hRequest){
cout<<"同步,HttpOpenRequest error"<<endl;//, "Err", MB_OK);
return;
}
if( ASYNC&& hRequest== NULL)// 異步 需要等待
{
int er = GetLastError();
DWORD dwError = ::GetLastError();
if (dwError != ERROR_IO_PENDING)
{
cout<<"CHttpDownload::OpenInternetConnection| 連接失敗" <<endl;
return ;
}
else //
{
cout<<"hRequest == NULL, so run WaitExitEvent"<<endl;
WaitExitEvent(); // 等待成功創(chuàng)建
::ResetEvent(hEvent[0]);
::ResetEvent(hEvent[1]);
::ResetEvent(hEvent[2]);
}
}
//Sleep(10000);
cout << "step 3 : HttpOpenRequest success"<<endl;
//::InternetSetStatusCallback(hRequest,InternetStatusCallback);
//////////////////////////////////////////////
if (setProxy )
{
// InternetSetOption 不要異步等待
if( !InternetSetOption(hRequest,INTERNET_OPTION_PROXY_USERNAME ,strUsername,strlen(strUsername)+1))
{
cout<<"InternetSetOption Username failed"<<endl;
return ;
}
if( !InternetSetOption(hRequest,INTERNET_OPTION_PROXY_PASSWORD ,strPassword,strlen(strPassword)+1))
{
cout<<"InternetSetOption Password failed"<<endl;
return ;
}
}
// step 4
//HttpSendRequest(hRequest,NULL,0,NULL,0);
//Sleep(3000);
::ResetEvent(hEvent[0]);
::ResetEvent(hEvent[1]);
::ResetEvent(hEvent[2]);
if(!::HttpSendRequest(hRequest,NULL,0,NULL,0)) // 為什么失敗???
{
//Sleep(3000);
if(!ASYNC)// 同步
{
DWORD dwError = ::GetLastError();
cout<<"同步,HttpSendRequest failed, GetLastError=="<<dwError<<endl;
return ;
}
else
{
Sleep(3000);
DWORD dwError = ::GetLastError();
cout<<"dwError =="<<dwError<<endl;
if (dwError != ERROR_IO_PENDING)
{
cout<<"dwError != ERROR_IO_PENDING, so quit,dwError =="<<dwError<<endl;
return ;
}
else //
{
cout<<"HttpSendRequest, so run WaitExitEvent"<<endl;
Sleep(3000);
//if(WaitExitEvent()!=2)//; // 等待成功創(chuàng)建 等待是否不對???
{
cout<<"had not recv complete event, so quit"<<endl;
// return ;
}
}
}
}
Sleep(3000);
cout << "step 4: HttpSendRequest success!"<<endl;
int bufh[1000];
DWORD dwLen,dwIndex;
/*if(!::HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, bufh, &dwLen, &dwIndex))// 這句話???
{
//return E_FAIL;
return;
}
*/
// 判斷狀態(tài)碼;
char m_dwStatusCode[90];
DWORD dwStatusSize = sizeof(m_dwStatusCode);
/*if (FALSE == ::HttpQueryInfo(hRequest, // 查詢失效??
HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER,
&m_dwStatusCode,
&dwStatusSize,
NULL)) //獲取返回狀態(tài)碼
{
return ;
}
//判斷狀態(tài)碼是不是 200
//if (HTTP_STATUS_OK != m_dwStatusCode)
{
//return ;
}
*/
DWORD dwByteToRead = 0;
DWORD dwSizeOfRq = 4;
DWORD dwBytes = 0;
//這三個值分別存儲文件的大小,HttpQueryInfo內(nèi)容的大小和總共讀取的字節(jié)數(shù)。
//HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwByteToRead, &dwSizeOfRq, NULL);
//需要說明的是 HttpQueryInfo 并不進行網(wǎng)絡(luò)操作,因此它不需要進行異步操作的處理。
if (!HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwByteToRead, &dwSizeOfRq, NULL))
{ // 這里失敗了???
DWORD dwError = ::GetLastError();
cout<<"HttpQueryInfo failed, so return, GetLastError() =="<<dwError<<endl;
return ;
}
FILE * pFile = fopen("e://baidu01.exe", "wb" );
//ofstream mfile("out.txt");//定義文件輸出流ouf,并關(guān)聯(lián)到out.txt
int i=0;
DWORD leftB = dwByteToRead;
cout<<"開始下載"<<endl;
if( !ASYNC) // 同步下載
{
while(true)
{
const int MAX_BUFFER_SIZE = 65536;
unsigned long nSize = 0;
char szBuffer[MAX_BUFFER_SIZE+2];
int num = MAX_BUFFER_SIZE;
if( leftB < num);
num = leftB;
BOOL bRet = ::InternetReadFile(hRequest, szBuffer, num, &nSize); // 異步 需要等待
leftB -= nSize;
cout<<i++<<" size: "<<nSize<<endl;
if(!bRet || nSize <= 0)
break;
fwrite(szBuffer, sizeof(char), nSize, pFile);
}
}
else // 異步下載
{
INTERNET_BUFFERS i_buf = {0};
i_buf.dwStructSize = sizeof(INTERNET_BUFFERS);
i_buf.lpvBuffer = new TCHAR[10242];
i_buf.dwBufferLength = 10240;
for( DWORD i=0;i<dwByteToRead;)
{
//重置讀數(shù)據(jù)事件
::ResetEvent( hEvent[0]);
int num = 10240;
if(dwByteToRead-i<10240)
{
num = dwByteToRead-i;
i_buf.dwBufferLength = dwByteToRead-i;
}
if (FALSE == ::InternetReadFileEx(hRequest,
&i_buf,
IRF_ASYNC,
NULL))
{
if (ERROR_IO_PENDING == ::GetLastError())
{
if ( NULL)//WaitExitEvent()!=2)
{
delete[] i_buf.lpvBuffer;
return ;
}
}
else
{
cout<<"down failed,so return"<<endl;
delete[] i_buf.lpvBuffer;
return ;
}
}
else
{
//在網(wǎng)絡(luò)傳輸速度快,步長較小的情況下,
//InternetReadFileEx 經(jīng)常會直接返回成功,
//因此要判斷是否發(fā)生了用戶要求終止子線程事件。
cout<<"網(wǎng)絡(luò)很好,InternetReadFileEx返回true"<<endl;
// 暫不考慮用戶退出
}
i += i_buf.dwBufferLength; // 最后一次寫多了!!!
fwrite(i_buf.lpvBuffer, sizeof(char), i_buf.dwBufferLength, pFile);
cout<<"i== "<<i<<endl;
//保存數(shù)據(jù)
//通知主線程下載進度
}
}
InternetCloseHandle(hRequest);
InternetCloseHandle(hConnect);
InternetCloseHandle(hSession);
cout<<"success download file"<<endl;
return;
}
int main( void )
{
WinINet3(true,true);
return 1;
}
void OnInternetHandleCreated(HINTERNET hInternet, LPINTERNET_ASYNC_RESULT lpInetStatusResult)
{
if(NULL == lpInetStatusResult)
{
//ATLASSERT( 0 );
return;
}
hFile = HINTERNET(lpInetStatusResult->dwResult);
HINTERNET hInet = HINTERNET(lpInetStatusResult->dwResult);
DWORD dwInetHandleType;
DWORD dwTypeLen = sizeof(dwInetHandleType);
InternetQueryOption( hInet, INTERNET_OPTION_HANDLE_TYPE, &dwInetHandleType, &dwTypeLen);
switch(dwInetHandleType)
{
case INTERNET_HANDLE_TYPE_CONNECT_HTTP:
//CloseInternetConnection(); // 這里是何意???? 通過回調(diào) 設(shè)置httpConnect
hConnect = hInet; //
break;
case INTERNET_HANDLE_TYPE_HTTP_REQUEST:
//CloseInternetFile(); // 這里是何意?? 通過回調(diào) 設(shè)置httpFile
hRequest = hInet; //
break;
default:
break;
}
cout<<"OnInternetHandleCreated, so ::SetEvent(hEvent[0])"<<endl;
// HANDLE已創(chuàng)建事件(異步控制)
::SetEvent(hEvent[0]);
}
void OnInternetRequestComplete(HINTERNET hInternet, LPINTERNET_ASYNC_RESULT lpInetStatusResult)
{
if( lpInetStatusResult == NULL )
{
//ATLASSERT( 0 );
return;
}
cout<<"OnInternetRequestComplete, so ::SetEvent(hEvent[2])"<<endl;
// 激發(fā)請求完成事件(異步控制)
::SetEvent(hEvent[0]);
}
void CALLBACK InternetStatusCallback(
HINTERNET hInternet,
DWORD_PTR dwContext,
DWORD dwInternetStatus,
LPVOID lpvStatusInformation,
DWORD dwStatusInformationLength
)
{
cout<<"進入回調(diào)"<<endl;
switch (dwInternetStatus)
{
case INTERNET_STATUS_RESOLVING_NAME:
break;
case INTERNET_STATUS_NAME_RESOLVED:
break;
case INTERNET_STATUS_CONNECTING_TO_SERVER:
break;
case INTERNET_STATUS_CONNECTED_TO_SERVER:
break;
case INTERNET_STATUS_SENDING_REQUEST:
break;
case INTERNET_STATUS_REQUEST_SENT:
break;
case INTERNET_STATUS_RECEIVING_RESPONSE:
break;
case INTERNET_STATUS_RESPONSE_RECEIVED:
break;
case INTERNET_STATUS_CLOSING_CONNECTION:
break;
case INTERNET_STATUS_CONNECTION_CLOSED:
break;
case INTERNET_STATUS_HANDLE_CREATED:
cout<<"回調(diào)是INTERNET_STATUS_HANDLE_CREATED"<<endl;
OnInternetHandleCreated(hInternet, LPINTERNET_ASYNC_RESULT(lpvStatusInformation)); // 傳遞了HINTERNET 這是精髓呀
break;
case INTERNET_STATUS_HANDLE_CLOSING:
break;
case INTERNET_STATUS_REQUEST_COMPLETE:
cout<<"回調(diào)是INTERNET_STATUS_REQUEST_COMPLETE"<<endl;
OnInternetRequestComplete(hInternet, LPINTERNET_ASYNC_RESULT(lpvStatusInformation));
break;
case INTERNET_STATUS_REDIRECT:
case INTERNET_STATUS_INTERMEDIATE_RESPONSE:
case INTERNET_STATUS_STATE_CHANGE:
default:
break;
}
}
posted on 2010-03-26 09:22
漂漂 閱讀(1497)
評論(0) 編輯 收藏 引用 所屬分類:
深入vc++