首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> VC 教學

WinInet 實現 HTTP 斷點續傳與多執行緒分段下載模組設計 (C++Builder)

     編輯:VC 教學

HTTP 協議基礎

  • GET:請求資源。支援 Range: bytes=start-end 斷點續傳。
  • HEAD:僅取得標頭 (Content-Length, Accept-Ranges)。
  • 伺服器回應 206 Partial Content 表示支援範圍請求。

WinInet API 核心句柄層級

  1. HINTERNET hSession = InternetOpen(...):會話句柄 (最上層)。
  2. HINTERNET hConnect = InternetConnect(hSession, ...):連線句柄。
  3. HINTERNET hRequest = HttpOpenRequest(hConnect, "GET", ...):請求句柄。
  4. HttpAddRequestHeaders(hRequest, "Range: bytes=1000-1999", ...):加入範圍標頭。
  5. HttpSendRequest(hRequest, ...):發送請求。
  6. InternetReadFile(hRequest, buf, size, &read):讀取資料。
  7. InternetCloseHandle 依序關閉三層句柄。

下載執行緒類別設計 (THttpGetThread)

class THttpGetThread : public TThread {
private:
    AnsiString FUrl, FOutFile;
    DWORD FStartPos, FEndPos;  // 此執行緒負責的位元組範圍 [StartPos, EndPos]
    HINTERNET FhSession, FhConnect, FhRequest;
    HANDLE FFileHandle;        // 目的檔案句柄 (共享寫入)
    bool FSuccess;
    DWORD FBytesDownloaded;

protected:
    virtual void __fastcall Execute();
    void __fastcall CleanupHandles();
    void __fastcall ReportProgress(DWORD bytes);  // Synchronize 回主執行緒更新 UI

public:
    __fastcall THttpGetThread(AnsiString Url, AnsiString OutFile,
                              DWORD StartPos, DWORD EndPos, HANDLE FileHandle);
    __fastcall ~THttpGetThread();
    bool Success() { return FSuccess; }
    DWORD BytesDownloaded() { return FBytesDownloaded; }
};

Execute 核心邏輯

void __fastcall THttpGetThread::Execute()
{
    FhSession = InternetOpen("MyDownloader/1.0", INTERNET_OPEN_TYPE_PRECONFIG, NULL, NULL, 0);
    if (!FhSession) return;

    // 解析 URL 取得 host, path, port (略,可用 InternetCrackUrl)

    FhConnect = InternetConnect(FhSession, Host, Port, User, Pass,
                                INTERNET_SERVICE_HTTP, 0, 0);
    if (!FhConnect) { CleanupHandles(); return; }

    FhRequest = HttpOpenRequest(FhConnect, "GET", Path, NULL, NULL, NULL,
                                INTERNET_FLAG_RELOAD | INTERNET_FLAG_NO_CACHE_WRITE, 0);
    if (!FhRequest) { CleanupHandles(); return; }

    // 設定 Range 標頭
    AnsiString rangeHdr = "Range: bytes=" + IntToStr(FStartPos) + "-" + IntToStr(FEndPos);
    HttpAddRequestHeaders(FhRequest, rangeHdr.c_str(), rangeHdr.Length(),
                          HTTP_ADDREQ_FLAG_ADD | HTTP_ADDREQ_FLAG_REPLACE);

    if (!HttpSendRequest(FhRequest, NULL, 0, NULL, 0)) { CleanupHandles(); return; }

    // 讀取迴圈
    const int BUFSZ = 8192;
    BYTE* buf = new BYTE[BUFSZ];
    DWORD dwRead = 0;
    OVERLAPPED ol = {0};
    ol.Offset = FStartPos;
    ol.OffsetHigh = 0;

    while (InternetReadFile(FhRequest, buf, BUFSZ, &dwRead) && dwRead > 0) {
        // 寫入檔案對應位置 (需檔案以 FILE_FLAG_OVERLAPPED 開啟)
        DWORD dwWritten;
        WriteFile(FFileHandle, buf, dwRead, &dwWritten, &ol);
        ol.Offset += dwWritten;
        FBytesDownloaded += dwWritten;
        if (FBytesDownloaded % 16384 == 0) Synchronize(&ReportProgress);
        if (Terminated) break;
    }
    delete[] buf;
    FSuccess = (FBytesDownloaded == (FEndPos - FStartPos + 1));
    CleanupHandles();
}

主控制器:分段策略與合併

  1. HEAD 請求取得 Content-Length、確認 Accept-Ranges: bytes。
  2. 決定執行緒數 N (如 4),計算每段大小 seg = total / N,最後一段含餘數。
  3. 建立目的檔案:CreateFile(..., FILE_FLAG_OVERLAPPED | FILE_FLAG_SEQUENTIAL_SCAN, ...),SetFilePointerEx 預分配大小 (或寫入時自動擴展)。
  4. 啟動 N 個 THttpGetThread,各自寫入對應區段 (使用 OVERLAPPED 指定 Offset)。
  5. 等待所有執行緒完成 (WaitForMultipleObjects 或輪詢 Thread->Finished)。
  6. 驗證總位元組數、MD5/CRC32 校驗 (可選)。
  7. 失敗段重試機制 (指數退避、最多 3 次)。

斷點續傳持久化

  • 下載前讀取 .dld 索引檔 (JSON/二進制),記錄各段 StartPos, EndPos, DownloadedBytes。
  • 啟動時跳過已完成段,僅下載未完成段。
  • 每段完成/定期更新索引檔。
  • 完成後刪除索引檔。

錯誤處理重點

  • InternetReadFile 返回 FALSE → GetLastError() 區分 ERROR_INTERNET_CONNECTION_RESET, ERROR_INTERNET_TIMEOUT, ERROR_HANDLE_EOF。
  • 伺服器不支援 Range → 回應 200 OK 而非 206 → 偵測並回退單執行緒完整下載。
  • 磁碟寫入失敗 → GetLastError() 檢查磁碟滿、權限。
  • UI 執行緒安全:所有 VCL 控件操作用 Synchronize 或 Queue。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved