WinINet操作流程

一 WinINet是干什么的?
二 如果完成一个基本的WinINet操作流程
三 如何异步完成,异步操作的好处。
四 unicode以及mutilbyte
五 代码样例

一 WinINet是微软开发的一个库,可以完成http ftp客户端的工作。让程序员从复杂的协议中节省大量体力。

二 我会用了http, ftp类似。用WinINet完成一个http下载需要以下步骤 。 下面这个是同步操作(也就是一步步操作,每个函数执行完才会返回一个结果的意思)

     1   InternetOpen      Initializes an application'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 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;
    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 = "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)// 异步 需要等待   竟然直接创建好了
    {
        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(); // 等待成功创建 // 这里应该等待   这里应该显示一次呀
            ::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(); // 等待成功创建
            ::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;
    /*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
    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;)
        {
            //重置读数据事件
            ::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_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_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;
    }
}

你可能感兴趣的:(WinINet操作流程)