WinInetシングルスレッドブレークポイントの継続ダウンロード

24573 ワード

最近暇なので、ネット上のプログラミングを試してみると、マルチスレッドのブレークポイントを書いてダウンロードを続ける簡単なdemoが興った.そこでまず,ネットワーク上で様々な実装構想を検索し,最終的には簡単な単線断点からダウンロードを継続することにした.
実現構想:
毎回、存在しない場合に作成するようにダウンロードするファイルを開き、そのサイズsizeを取得し、URLにリクエストヘッダを送信し、ヘッダ情報rangeを付け、sizeを取得するデータの開始位置とし、終了位置は書かない(後のすべてのデータを取得することを示す).そして、ファイルの最後にデータを書き続けます.あるダウンロードが開始されるまでに返されるステータスコードは416であり、範囲外であることを示すと、ファイルのダウンロードが完了したことを示す.
重要なポイント:
1、HTTPプロトコルに対して初歩的な理解が必要で、主に要求ヘッダと返却ヘッダのフォーマット要求を理解する.
2、Httpリクエストヘッダのrangeキーワードの使い方を理解する.
3、Goole Chromeの審査要素機能とマイクロソフトのwfetch小道具を使ってHttpリクエストヘッダとリターンヘッダ情報の表示を行うことを学ぶ.
4、最后に、もちろんプログラミングの知识で、wininetライブラリの使用と基本的なスレッドとファイルの操作などが含まれています.
 
残りはこの考え方に沿って、ゆっくりと関連知識を勉強して、それからプログラミングしてこのDemoを実現しました.私はここに自分のコードを貼って、私は英語が本当に下手ですが、やはり英語を使って注釈をつけてみました.よく読めないので、私に聞いてもいいです.
/*

    Filename:

            main.cpp



    Function:

                       。

                    (      )                 ,                  。



    Knowledge:

                HTTP    。

            HTTP     verb(  )   HEADER     ,              ,     。

            HTTP Header   range               。   "Range: bytes=StartPos-EndPos\r
" "EndPos" , 。 StartPos , ”416 Requested Range Not Satisfiable“。 Range 206。 EndPos==StartPos , 1 。 EndPos>StartPos , 。 History: time: 2012/11/26 remarks: test finish auth: monotone
*/ #include <Windows.h> #include <wininet.h> #include <stdio.h> #include <string> #include <iostream> #include <tchar.h> using namespace std; #pragma comment(lib, "wininet.lib") const char* STR_TEST_URL = "http://dl_dir.qq.com/qqfile/qq/QQ2013/QQ2013Beta1.exe" // QQ , , , 。 const DWORD DWORD_MAX_CCH_OF_TEST_URL = 256; const DWORD DWORD_MAX_CCH_OF_HOST_NAME = 128; const DWORD DWORD_MAX_CCH_OF_URL_PATH = 256; BOOL GetWininetLastErrorMsgA(OUT string& rStrErrorMsg) { BOOL lbResult = FALSE; char* lscErrorMsg = NULL; if(0 != FormatMessageA( FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER, // dwFlags GetModuleHandle( TEXT("wininet.dll") ), // lpSource GetLastError(), // dwMessageId 0, // dwLanguageId (LPTSTR)&lscErrorMsg, // lpBuffer 0, // nSize NULL)) { rStrErrorMsg = lscErrorMsg; lbResult = TRUE; } if(NULL != lscErrorMsg) LocalFree(lscErrorMsg); return lbResult; } int main() { HINTERNET hInetOpen = NULL; HINTERNET hInetConnect = NULL; HINTERNET hInetRequest = NULL; HANDLE lhFile = NULL; do { // struct to contains the constituent parts of a URL URL_COMPONENTS ldCrackedURL; ZeroMemory(&ldCrackedURL, sizeof(URL_COMPONENTS)); ldCrackedURL.dwStructSize = sizeof(URL_COMPONENTS); // // buffer to store host name TCHAR szHostName[DWORD_MAX_CCH_OF_HOST_NAME] = {0}; ldCrackedURL.lpszHostName = szHostName; ldCrackedURL.dwHostNameLength = DWORD_MAX_CCH_OF_HOST_NAME; // // buffer to store url path char szUrlPath[DWORD_MAX_CCH_OF_URL_PATH] = {0}; ldCrackedURL.lpszUrlPath = szUrlPath; ldCrackedURL.dwUrlPathLength = DWORD_MAX_CCH_OF_URL_PATH; // // Ulr 。 URL_COMPONENTS , 。 , , 。 // URL_COMPONENTS NULL, dwStructSize 0, , , 。 // "file://" URL 。 if(FALSE == InternetCrackUrlA(STR_TEST_URL, (DWORD)strlen(STR_TEST_URL), 0, &ldCrackedURL)) { // GetLastError(); break; } // Get file name, , ulr url。 string loStrFileName(ldCrackedURL.lpszUrlPath); string::size_type liFileNamePos = loStrFileName.rfind("/"); if(string::npos != liFileNamePos) { loStrFileName = loStrFileName.substr(liFileNamePos + 1, string::npos); } // open internet hInetOpen = InternetOpenA("Breakpoint Continue Dounload Sample", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0); if(NULL == hInetOpen) { // GetLastError(); break; } // connect server hInetConnect = InternetConnectA(hInetOpen, ldCrackedURL.lpszHostName, ldCrackedURL.nPort, NULL, NULL, INTERNET_SERVICE_HTTP, 0, 0); if(NULL == hInetConnect) { // GetLastError(); break; } /* test 1: Read the destination file size as the size of data which download last(if file not exist, set the size to zero). And set the value argument of HTTP Header "Range: bytes=value-\r
" to "size - 1"(the size position). Then open(or create) the file and append data from the end until no data to read, it means file download over.
*/ // Get the file size lhFile = CreateFileA(loStrFileName.c_str(), GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); if(lhFile == INVALID_HANDLE_VALUE) { break; } LARGE_INTEGER ldFileSize; if(FALSE == GetFileSizeEx(lhFile, &ldFileSize)) { break; } // make the start position LONGLONG lllStartPos = 0; if(0 == ldFileSize.QuadPart) { cout << "new file to download. " << endl; } else { // Set the file pointer position if(INVALID_SET_FILE_POINTER == SetFilePointer(lhFile, 0, NULL, FILE_END)) { cout << "Move file pointer failed. " << endl; break; } lllStartPos = ldFileSize.QuadPart; cout << "continue to download from position:" << lllStartPos << endl; } // convert the range start position to character char lscRangeStartPosition[30] = {0}; if(0 != _i64toa_s((__int64)(lllStartPos), lscRangeStartPosition, sizeof(lscRangeStartPosition), 10)) { break; } // additional header: set the file data range . string loAdditionalHeader = "Range: bytes="; loAdditionalHeader += lscRangeStartPosition; // start position of remaining loAdditionalHeader += "-\r
"; // open request with "GET" verb to get the remaining file data const char* lplpszAcceptTypes[] = {"*/*", NULL}; hInetRequest = HttpOpenRequestA(hInetConnect, "GET", ldCrackedURL.lpszUrlPath, "HTTP/1.1", NULL, lplpszAcceptTypes, 0, 0); if(NULL == hInetConnect) { // GetLastError(); break; } // send request with additional header if(FALSE == HttpSendRequestA(hInetRequest, loAdditionalHeader.c_str(), loAdditionalHeader.size(), NULL, 0)) { // GetLastError(); break; } // query the status code from the reponse of servers DWORD ldwStatusCode; DWORD ldwCbOfStatusCode = sizeof(ldwStatusCode); if(FALSE == HttpQueryInfo(hInetRequest, HTTP_QUERY_STATUS_CODE | HTTP_QUERY_FLAG_NUMBER, &ldwStatusCode, &ldwCbOfStatusCode, 0)) { break; } //HTTP_QUERY_CONTENT_RANGE //// get file size // check the status code if(416 == ldwStatusCode) // 416 Requested Range Not Satisfiable { cout << "the file does not need to download." << endl; break; } else if(200 != ldwStatusCode && 206 != ldwStatusCode) // 206 Partial Content { // statuscode means error occurred. break; } // loop to read the data from HTTP and store to file BYTE lpbBufferToReceiveData[2048]; // Buffer DWORD ldwCbBuffer = 2048; DWORD ldwCrtCbReaded; // DWORD ldwCbWritten = 0; // bool lbIsOk = false; LONGLONG lllCbAllRead = 0; do { // read data if (FALSE == InternetReadFile(hInetRequest, lpbBufferToReceiveData, ldwCbBuffer, &ldwCrtCbReaded)) { cout << "read data failed." << endl; break; } if(ldwCrtCbReaded == 0) // all data haved been read. { cout << "Congratulation! file download finish successfully." << endl; break; } // write to file if(FALSE == WriteFile(lhFile, lpbBufferToReceiveData, ldwCrtCbReaded, &ldwCbWritten, NULL) || ldwCbWritten != ldwCrtCbReaded) { cout << "A exception happens when write data to file" << endl; break; } // clear data in buffer ZeroMemory(lpbBufferToReceiveData, ldwCrtCbReaded); lllCbAllRead += ldwCrtCbReaded; cout << "crt readed data size:——————————" << lllCbAllRead / 1048576 << "MB" << endl; } while (true); }while(false); string loStrErrorMsg; if(FALSE != GetWininetLastErrorMsgA(loStrErrorMsg)) { cout << loStrErrorMsg.c_str() << endl; } if(NULL != lhFile) { CloseHandle(lhFile); } if(NULL != hInetRequest) { InternetCloseHandle(hInetRequest); } if(NULL != hInetConnect) { InternetCloseHandle(hInetConnect); } if(NULL != hInetOpen) { InternetCloseHandle(hInetOpen); } getchar(); return 0; }

 
不足点:
1、サーバー側がブレークポイントの継続をサポートしていないことを検出していない.
2、現在のネットワーク状態を検出していない.
3、プロセス全体のエラー検出がはっきりしていない.