天天看點

wininet異步代理的程式設計

// testwinInet.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <WINDOWS.H>
#include <WININET.H>
#include <STDLIB.H>
#include <string>
#include <IOSTREAM.H>

#pragma comment(lib,"wininet.lib")

using namespace std;

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)
    {
        //句柄被建立事件或者讀資料請求成功完成事件
    case WAIT_OBJECT_0:
        x=0;
        cout<<"WAIT_OBJECT_0"<<endl;
        //句柄被關閉事件
        break;
    case WAIT_OBJECT_0+1:
        x=1;
        cout<<"WAIT_OBJECT_1"<<endl;
        //使用者要求終止子線程事件或者發生錯誤事件
        break;
    case WAIT_OBJECT_0+2:
        x=2;
        cout<<"WAIT_OBJECT_2"<<endl;
        break;
    default:
        cout<<"WaitForMultipleObjects time out"<<endl;
        return -1;
		
    }
    return x;
}

// 支援代理設定, 是否異步設定; 采用事件驅動
void WinINet3(bool setProxy, bool ASYNC)
{
    hSession=NULL;
    hConnect=NULL;
    hRequest=NULL;
	int i;
    for (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 = "http://down.360safe.com/setup.exe";
    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個參數可以輸入使用者名,密碼"administrator","password"
    //第2,3個參數為目标主機IP、端口号(不是代理伺服器的參數)
    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)// 異步需要等待   竟然直接建立好了
    {
        DWORD dwError = ::GetLastError();
        if (dwError != ERROR_IO_PENDING)
        {
            cout<<"CHttpDownload::OpenInternetConnection| 連接配接失敗" <<endl;
            return ;
        }
        else //
        {
            cout<<"hConnect == NULL, so run WaitExitEvent"<<endl;
            WaitExitEvent(); // 等待成功建立 // 這裡應該等待   這裡應該顯示一次呀
            ::ResetEvent(hEvent[0]);
            ::ResetEvent(hEvent[1]);
            ::ResetEvent(hEvent[2]);
        }
    }
    cout<<"step 2 :InternetConnect succeed"<<endl;

    // ::InternetSetStatusCallback(hConnect,InternetStatusCallback);
   
    // step 3!!!
    char   szHead[] = "Accept: *NULL,0,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);
   
    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(); // 等待成功建立
            ::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)//; // 等待成功建立 等待是否不對???
                {
                       cout<<"had not recv complete event, so quit"<<endl;
                    // return ;
                }
            }
        }
       
    }
    Sleep(3000);
    cout << "step 4: HttpSendRequest success!"<<endl;

    int bufh[1000];
    DWORD dwLen,dwIndex;
   
    // 判斷狀态碼;
    char m_dwStatusCode[90];
    DWORD dwStatusSize = sizeof(m_dwStatusCode);
   
    DWORD dwByteToRead = 0;
    DWORD dwSizeOfRq = 4;
    DWORD dwBytes = 0;
    //這三個值分别存儲檔案的大小,HttpQueryInfo内容的大小和總共讀取的位元組數。
    //HttpQueryInfo(hRequest, HTTP_QUERY_CONTENT_LENGTH | HTTP_QUERY_FLAG_NUMBER, (LPVOID)&dwByteToRead, &dwSizeOfRq, NULL);
    //需要說明的是 HttpQueryInfo 并不進行網絡操作,是以它不需要進行異步操作的處理。
    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("d://baidu01.exe", "wb" );
    //ofstream mfile("out.txt");//定義檔案輸出流ouf,并關聯到out.txt
    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;)
        {
            //重置讀資料事件
            ::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
            {
                //在網絡傳輸速度快,步長較小的情況下,
                //InternetReadFileEx 經常會直接傳回成功,
                //是以要判斷是否發生了使用者要求終止子線程事件。
                cout<<"網絡很好,InternetReadFileEx傳回true"<<endl;

                // 暫不考慮使用者退出
            }
            i += i_buf.dwBufferLength; // 最後一次寫多了!!!
            fwrite(i_buf.lpvBuffer, sizeof(char), i_buf.dwBufferLength, pFile);
            cout<<"i== "<<i<<endl;
            //儲存資料
             //通知主線程下載下傳進度
                   
        }
    }
    InternetCloseHandle(hRequest);
    InternetCloseHandle(hConnect);
    InternetCloseHandle(hSession);
    cout<<"success download file"<<endl;
   
    return;
}


int main(int argc, char* argv[])
{
	printf("Starting .......!\n");

	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(); //   這裡是何意???? 通過回調 設定httpConnect
        hConnect = hInet;     //
        break;
    case INTERNET_HANDLE_TYPE_HTTP_REQUEST:
        //CloseInternetFile();    //    這裡是何意??    通過回調設定httpFile
        hRequest = hInet;    //
        break;
    default:
        break;
    }
    cout<<"OnInternetHandleCreated, so ::SetEvent(hEvent[0])"<<endl;
    // HANDLE已建立事件(異步控制)
    ::SetEvent(hEvent[0]);
}
void OnInternetRequestComplete(HINTERNET hInternet, LPINTERNET_ASYNC_RESULT lpInetStatusResult)
{
	
    if( lpInetStatusResult == NULL )
    {
        //ATLASSERT( 0 );
        return;
    }
    cout<<"OnInternetRequestComplete, so ::SetEvent(hEvent[2])"<<endl;
    // 激發請求完成事件(異步控制)
    ::SetEvent(hEvent[0]);
}

void CALLBACK InternetStatusCallback(
									 HINTERNET hInternet,
									 DWORD dwContext,
									 DWORD dwInternetStatus,
									 LPVOID lpvStatusInformation,
									 DWORD dwStatusInformationLength
									 )
{
    cout<<"進入回調"<<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<<"回調是INTERNET_STATUS_HANDLE_CREATED"<<endl;
        OnInternetHandleCreated(hInternet, LPINTERNET_ASYNC_RESULT(lpvStatusInformation));
        break;
    case INTERNET_STATUS_HANDLE_CLOSING:
        break;
    case INTERNET_STATUS_REQUEST_COMPLETE:
        cout<<"回調是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;
    }
}