單線程下載受限於伺服器單連接頻寬限制、網路延遲、串行傳輸效率低。多線程下載透過分段並行請求(HTTP Range 標頭),將檔案切分為多個區塊同時下載,再合併還原,可顯著提升吞吐量,這正是「網路螞蟻」「迅雷」等下載工具的核心原理。
HTTP/1.1 定義 Range 標頭,允許客戶端請求資源的特定位元組範圍:
Range: bytes=0-1023 // 前 1024 位元組
Range: bytes=1024-2047 // 第二段
Range: bytes=1024- // 從 1024 到結尾
伺服器支援時回應 206 Partial Content,並附上 Content-Range 與 Content-Length。若不支援則回 200 OK 全檔傳輸(此時多線程無效)。
AddRange)、送出請求。GetResponseStream)、讀取 ContentLength。System.Net 命名空間,屬抽象類 WebRequest/WebResponse 的 HTTP 實作。包含:URL 輸入框、儲存路徑、線程數輸入、開始按鈕、狀態列表框。
public bool[] threadDone; // 各線程完成標記
public string[] tempFiles; // 暫存檔路徑
public int[] startPos; // 起始位元組
public int[] blockSize; // 區塊大小
public string targetUrl; // 下載 URL
public int threadCount; // 線程數
public class HttpFile {
public Form1 Form { get; set; }
public int ThreadIndex { get; set; }
public void Receive() {
string tempFile = Form.tempFiles[ThreadIndex];
string url = Form.targetUrl;
int start = Form.startPos[ThreadIndex];
int size = Form.blockSize[ThreadIndex];
Form.Log($"線程 {ThreadIndex} 開始下載:{start} - {start + size}");
using (FileStream fs = new FileStream(tempFile, FileMode.Create))
using (var request = (HttpWebRequest)WebRequest.Create(url)) {
request.AddRange(start, start + size - 1); // 關鍵:設定 Range
using (var response = (HttpWebResponse)request.GetResponse())
using (Stream ns = response.GetResponseStream()) {
byte[] buffer = new byte[4096]; // 4KB 緩衝區
int read;
while ((read = ns.Read(buffer, 0, buffer.Length)) > 0) {
fs.Write(buffer, 0, read);
}
}
}
Form.Log($"線程 {ThreadIndex} 下載完成");
Form.threadDone[ThreadIndex] = true;
}
}
private void btnStart_Click(object sender, EventArgs e) {
targetUrl = txtUrl.Text.Trim();
threadCount = int.Parse(txtThreadCount.Text);
// 1. 先取得檔案總大小
long totalSize;
using (var req = (HttpWebRequest)WebRequest.Create(targetUrl))
using (var resp = (HttpWebResponse)req.GetResponse()) {
totalSize = resp.ContentLength;
req.Abort(); // 僅取標頭,釋放連線
}
// 2. 平均分段(最後一段含餘數)
int avg = (int)(totalSize / threadCount);
int last = avg + (int)(totalSize % threadCount);
threadDone = new bool[threadCount];
tempFiles = new string[threadCount];
startPos = new int[threadCount];
blockSize = new int[threadCount];
for (int i = 0; i < threadCount; i++) {
threadDone[i] = false;
tempFiles[i] = $"{i}.part";
startPos[i] = i * avg;
blockSize[i] = (i == threadCount - 1) ? last : avg;
}
// 3. 啟動下載線程
for (int i = 0; i < threadCount; i++) {
var httpFile = new HttpFile { Form = this, ThreadIndex = i };
new Thread(httpFile.Receive).Start();
}
// 4. 啟動合併監控線程
new Thread(MergeFiles).Start();
}
private void MergeFiles() {
// 等待所有線程完成
while (true) {
bool allDone = true;
for (int i = 0; i < threadCount; i++) {
if (!threadDone[i]) { allDone = false; Thread.Sleep(100); break; }
}
if (allDone) break;
}
string savePath = txtSavePath.Text.Trim();
using (FileStream fs = new FileStream(savePath, FileMode.Create)) {
byte[] buffer = new byte[4096];
for (int i = 0; i < threadCount; i++) {
using (FileStream part = new FileStream(tempFiles[i], FileMode.Open)) {
int read;
while ((read = part.Read(buffer, 0, buffer.Length)) > 0) {
fs.Write(buffer, 0, read);
}
}
File.Delete(tempFiles[i]); // 清理暫存檔
}
}
MessageBox.Show("下載合併完成!");
}
WebException 並實作指數退避重試。Receive 迴圈中回報已下載位元組,UI 執行緒透過 Invoke 更新進度條。AddRange(start + downloaded, end) 接續。HttpClient + Stream.CopyToAsync,支援非同步、連線池、更簡潔。本文從 HTTP Range 原理出發,完整實作了 C# 多線程下載器:取得長度 → 計算分段 → 並行 Range 請求 → 暫存檔 → 合併清理。掌握此核心流程,即可自行構建高效下載工具,並為斷點續傳、限速、多來源下載等進階功能奠定基礎。