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