WinInet開發中的同步和異步區別

同步和異步的概念在此就不囉唆了。
以下載一個文件爲例,我們來看看同步的做法:
1)InternetOpen;
2)InternetOpenUrl;
3)HttpQueryInfo;
4)InternetReadFile;
5)InternetCloseHandle。
在第2步和第4步,程序會一直等待,直到函數返回。如果要設置超時,可以使用InternetSetOption(不過好像沒什麼用)。在很多時候,這個函數是不合適的。比如用戶主動要中斷下載,卻只能等待函數返回。還有,如果是大文件下載,無法想像一次讀取上兆字節的數據,需使用斷點續傳,雖然也可以使用同步函數InternetSetFilePointer來定位網絡文件讀取位置,但很多服務器是不支持的。如果在手機上使用,還要考慮諸如移動網關的限制等等。
同步的好處是函數較少,流程清晰,調試也方便。

再來看看異步的做法。
1)InternetOpen,需指定是異步;
2)InternetSetStatusCallback,設置回調;
3)InternetOpenUrl,需指定回調參數;
4)WaitForSingObject或WaitForMultipleObjects,接收信號量;
5)HttpQueryInfo;
6)InternetReadFileEx,需指定回調參數;
7)WaitForSingObject或WaitForMultipleObjects,接收信號量;
8)InternetSetStatusCallback,卸載回調;
9)InternetCloseHandle。
可以看出,異步比同步要複雜了不少,重點在於回調函數。在回調中,系統會及時返回各種系統定義的HTTP消息,我們根據這些消息來設置某些信號量。在WaitForSingObject或WaitForMultipleObjects裏,等待這些信號(當然也可以等待用戶的取消動作)。當有正確的信號返回時,繼續往下的操作。
異步方式下,InternetOpenUrl可以在header頭裏設置要讀取的範圍。比如讀0到1024的數據,在header頭裏加入Range: bytes=0-1024/r/n。這種方式保證了斷點續傳。要注意的是,如果服務器支持斷點續傳,此時使用HttpQueryInfo得到的狀態碼是206,而不是200。

回調函數怎麼寫啊?看看微軟提供的一個例子吧。這個例子是用POST的方式上傳數據,比上述下載數據步驟更爲麻煩,把InternetOpenUrl這個函數分成更多的函數來處理。有時間再挖挖這個例子。微軟這個例子是在一個sendreqexasync.cpp的文件中,在微軟的網站上應該可以得到。

省去其他,我們單單看看回調的寫法:
......
void __stdcall Callback(HINTERNET hInternet,
              DWORD dwContext,
              DWORD dwInternetStatus,
              LPVOID lpStatusInfo,
              DWORD dwStatusInfoLen)
{
    cout << "Callback dwInternetStatus: " << dwInternetStatus << " Context: " << dwContext << endl;
    cout.flush();

    switch(dwContext)
    {
    case 1: // Connection handle
        if (dwInternetStatus == INTERNET_STATUS_HANDLE_CREATED)
        {
            INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
            hConnect = (HINTERNET)pRes->dwResult;
            cout << "Connect handle created" << endl;
            cout.flush();
            SetEvent(hConnectedEvent);
        }
        break;

    case 2: // Request handle
        switch(dwInternetStatus)
        {
        case INTERNET_STATUS_HANDLE_CREATED:
            {
                INTERNET_ASYNC_RESULT *pRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
                hRequest = (HINTERNET)pRes->dwResult;
                cout << "Request handle created" << endl;
                cout.flush();
            }
            break;

        case INTERNET_STATUS_REQUEST_SENT:
            {
                DWORD *lpBytesSent = (DWORD*)lpStatusInfo;
                cout << "Bytes Sent: " << *lpBytesSent << endl;
                dwNumBytesComplete += *lpBytesSent;
            }
            break;

        case INTERNET_STATUS_REQUEST_COMPLETE:
            {
                INTERNET_ASYNC_RESULT *pAsyncRes = (INTERNET_ASYNC_RESULT *)lpStatusInfo;
                cout << "Function call finished" << endl;
                cout << "dwResult: " << pAsyncRes->dwResult << endl;
                cout << "dwError:  " << pAsyncRes->dwError << endl;
                cout.flush();
                SetEvent(hRequestCompleteEvent);
            }
            break;

        case INTERNET_STATUS_RECEIVING_RESPONSE:
            cout << "Receiving Response" << endl;
            cout.flush();
            break;

        case INTERNET_STATUS_RESPONSE_RECEIVED:
            {
                DWORD *dwBytesReceived = (DWORD*)lpStatusInfo;
                cout << "Received " << *dwBytesReceived << endl;
                cout.flush();
            }
        }
    }
}

 

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