WinInet

一 WinINet是幹什麼的?
二如果完成一個基本的WinINet操作流程
三如何異步完成,異步操作的好處。
四 unicode以及mutilbyte
五代碼樣例

一 WinINet是微軟開發的一個庫,可以完成http ftp客戶端的工作。讓程序員從複雜的協議中節省大量體力。

二我會用了http, ftp類似。用WinINet完成一個http下載需要以下步驟 。 下面這個是同步操作(也就是一步步操作,每個函數執行完纔會返回一個結果的意思)

    1   InternetOpen      Initializes anapplication's use of the WinINet functions.
                  需要的話 InternetSetOption 設置代理服務器地址以及端口。
                  http:    ip:port 或者 http=http://ip:port
                  socks:SOCKS=ip:port
    2   InternetConnect    關聯目標地址或者域名以及服務ip

    3   HttpOpenRequest   關聯要下載的內容名字
                  InternetSetOption 設置用戶名密碼

    4   HttpSendRequest     這步就是用HttpOpenRequest 的返回值(已經關聯了上面的所有信息)發送出去,第一次用了網絡。向目標服務器或者代理服務器。

    5   HttpQueryInfo
           該函數查詢返回值,不參與網絡操作。可以查詢服務器的返回信息,比如目標文件的大小,該文件是否存在,代理服務返回了要求用戶名,密碼等等(這幾個最常用),還有很多信息。

    6   InternetReadFile
           很普通的讀函數,就是下載文件。不知道是否和底層網絡同步,底層會不會提前下載呢?

    7   InternetCloseHandle 釋放資源

三    異步操作,比較複雜的。   爲什麼需要異步操作呢? 因爲涉及到網絡操作,某些函數在操作中可能需要時間,如果一直不返回(比如1秒)時,這時主線程要結束程序,豈不就出現意想不到的結果了,但是如果每個函數都能夠瞬間返回,然後通過WaitForMultipleObjects或者WaitForSingleObject等待結果的出現(此時就不會操作那個消耗1秒的函數了,而這個1秒函數正是要用到系統資源HINTERNET的)。

      異步操作的目的上如,原理呢? 其實原理就是註冊一個函數,在這裏叫InternetStatusCallback,因爲微軟寫得底層代碼要用到,所以必須格式統一。一旦有結果來了就通過事件通知我們,WaitForSingleObject函數就可以走了。比如等到了HINTERNET創建或者命令發送成功等結果。然後我們就可以第一時間安全的使用了。
     API 函數如果名字最後可以帶EX,那麼帶ex的就是異步操作的。

四一定要注意,凡是有unicode和mutilbyte函數的一定要統一,最好都用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 CALLBACKInternetStatusCallback(

                                 HINTERNET hInternet,
                                 DWORD dwContext,
                                 DWORD dwInternetStatus,
                                 LPVOID lpvStatusInformation,
                                 DWORD dwStatusInformationLength);
HANDLE hEvent[3];

HINTERNET hFile;
HINTERNET hNet;
HINTERNEThSession,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 timeout"<<endl;
       return -1;

    }
   return x;
}

// 支持代理設置, 是否異步設置; 採用事件驅動
void WinINet3(boolsetProxy, bool ASYNC)
{
   hSession=NULL;
   hConnect=NULL;
   hRequest=NULL;
   for (int i = 0; i < 3; i++) 
   { 
       hEvent[i] = CreateEvent( 
           NULL,   // default securityattributes
           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<<"InternetSetStatusCallbacksucceed, 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<<"同步,InternetConnecterror"<<endl;//, "Err", MB_OK);
       return;
    }
   if( ASYNC&& hConnect== NULL)// 異步 需要等待   竟然直接創建好了
    {
       int er = GetLastError();
       DWORD dwError = ::GetLastError();
       if (dwError != ERROR_IO_PENDING) 
       {
          cout<<"CHttpDownload::OpenInternetConnection| 連接失敗" <<endl;
           return ;
       }
       else //
       {
           cout<<"hConnect == NULL, sorun WaitExitEvent"<<endl;
           WaitExitEvent(); // 等待成功創建 // 這裏應該等待   這裏應該顯示一次呀
           ::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,/*(constchar **)p*/NULL,0/*INTERNET_FLAG_NO_COOKIES|INTERNET_FLAG_RELOAD*/,0); // norequest;
   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<<"同步,HttpOpenRequesterror"<<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, sorun 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<<"InternetSetOptionUsername failed"<<endl;
           return ;
       }
       if(!InternetSetOption(hRequest,INTERNET_OPTION_PROXY_PASSWORD,strPassword,strlen(strPassword)+1))
       {
           cout<<"InternetSetOptionPassword 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;
    /*if(!::HttpQueryInfo(hRequest,HTTP_QUERY_RAW_HEADERS_CRLF, bufh, &dwLen, &dwIndex))// 這句話???
    {
       //return E_FAIL;
       return;
    }
*/
   // 判斷狀態碼;
   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))   //獲取返回狀態碼
    {
       return ;
    }
   //判斷狀態碼是不是 200
   //if (HTTP_STATUS_OK != m_dwStatusCode)
    {
       //return ;
    }
*/

   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("e://baidu01.exe", "wb" );
   //ofstream mfile("out.txt");//定義文件輸出流ouf,並關聯到out.txt
    inti=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( void )
{
    
   WinINet3(true,true);
   return 1;
}

void OnInternetHandleCreated(HINTERNET hInternet, LPINTERNET_ASYNC_RESULTlpInetStatusResult)
{
   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_PTR 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)); // 傳遞了HINTERNET 這是精髓呀
       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;
    }
}

謝謝!


   網址:http://blog.csdn.net/jiht594/article/details/7481889

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章