在嵌入式设备固件升级、大文件下载场景中,网络不稳定导致下载中断后重新下载整个文件,浪费时间与带宽。
断点续传原理
关键HTTP头字段
交互流程
客户端 服务器 | | |-------- HEAD /file.zip --------->| 检查是否支持断点续传 |<------- Accept-Ranges: bytes ----| |<------- Content-Length: 50000 ---| | | |-------- Range: bytes=0-1023 ---->| 请求前1024字节 |<------- Content-Range: bytes 0-1023/50000 ----| |<------- [1024字节数据] ----------| | | |-------- Range: bytes=1024- ----->| 从1024字节开始续传 |<------- Content-Range: bytes 1024-49999/50000 ----| |<------- [剩余数据] ---------------|curl断点续传
自动续传
使用-C -选项,curl自动检测本地已下载文件大小,从断点处继续下载。
curl -C - -O http://example.com/firmware.bin原理:
1. 检查本地是否存在 firmware.bin2. 获取本地文件大小(如已下载2048字节) 3. 发送请求: Range: bytes=2048-4. 服务器返回206 Partial Content状态码 5. 将新数据追加到本地文件末尾
手动指定续传位置
使用-C <offset>指定从特定字节位置开始下载。
curl -C 1024 -o firmware.bin http://example.com/firmware.bin适用场景:
• 本地文件损坏,需要从特定位置重新下载 • 已知准确的断点位置 • 需要精确控制下载范围
范围下载
使用--range或-r选项下载指定范围的数据。
curl --range 0-1048575 -o part1.bin http://example.com/largefile.bincurl --range 1048576-2097151 -o part2.bin http://example.com/largefile.bin适用场景:
• 多线程分段下载 • 下载文件的特定部分 • 并行下载加速
具体实现
嵌入式设备固件升级
在嵌入式Linux设备上实现可靠的固件下载:
#!/bin/bashFIRMWARE_URL="http://ota.example.com/firmware_v2.1.bin"FIRMWARE_FILE="firmware.bin"MAX_RETRY=5RETRY_COUNT=0while [ $RETRY_COUNT -lt $MAX_RETRY ]; doecho"下载尝试: $((RETRY_COUNT + 1))/$MAX_RETRY" curl -C - --connect-timeout 30 --max-time 600 \ -o "$FIRMWARE_FILE""$FIRMWARE_URL" CURL_EXIT_CODE=$?if [ $CURL_EXIT_CODE -eq 0 ]; thenecho"下载完成"breakelif [ $CURL_EXIT_CODE -eq 18 ]; thenecho"部分传输完成,准备续传..." RETRY_COUNT=$((RETRY_COUNT + 1))sleep 5elseecho"下载失败,错误码: $CURL_EXIT_CODE"exit 1fidoneif [ $RETRY_COUNT -eq $MAX_RETRY ]; thenecho"达到最大重试次数,下载失败"exit 1fiecho"验证文件完整性..."md5sum -c firmware.md5参数说明:
• --connect-timeout 30:连接超时30秒• --max-time 600:总传输时间不超过10分钟• -C -:自动续传• 退出码18:表示传输中断但部分数据已保存
大文件分段下载
对于超大文件(如系统镜像),使用分段下载提高可靠性:
#!/bin/bashURL="http://mirror.example.com/ubuntu-22.04.iso"OUTPUT="ubuntu-22.04.iso"TOTAL_SIZE=$(curl -sI "$URL" | grep -i "Content-Length" | awk '{print $2}' | tr -d '\r')CHUNK_SIZE=$((10 * 1024 * 1024)) # 10MB每块echo"文件总大小: $TOTAL_SIZE 字节"rm -f "$OUTPUT"touch"$OUTPUT"for ((offset=0; offset<TOTAL_SIZE; offset+=CHUNK_SIZE)); do end=$((offset + CHUNK_SIZE - 1))if [ $end -ge $TOTAL_SIZE ]; then end=$((TOTAL_SIZE - 1))fiecho"下载范围: $offset-$end" curl --range "$offset-$end" -s -o temp_chunk "$URL"cat temp_chunk >> "$OUTPUT"rm -f temp_chunkdoneecho"下载完成,验证文件大小..."ACTUAL_SIZE=$(stat -c%s "$OUTPUT")if [ "$ACTUAL_SIZE" -eq "$TOTAL_SIZE" ]; thenecho"文件大小验证通过"elseecho"文件大小不匹配: 期望 $TOTAL_SIZE, 实际 $ACTUAL_SIZE"fi检测服务器是否支持断点续传
#!/bin/bashURL="http://example.com/file.zip"SUPPORT_RANGE=$(curl -sI "$URL" | grep -i "Accept-Ranges" | awk '{print $2}' | tr -d '\r')if [ "$SUPPORT_RANGE" = "bytes" ]; thenecho"服务器支持断点续传" FILE_SIZE=$(curl -sI "$URL" | grep -i "Content-Length" | awk '{print $2}' | tr -d '\r')echo"文件大小: $FILE_SIZE 字节" curl -C - -O "$URL"elseecho"服务器不支持断点续传,使用普通下载" curl -O "$URL"filibcurl编程实现
在嵌入式C程序中使用libcurl实现断点续传:
#include<stdio.h>#include<curl/curl.h>#include<sys/stat.h>size_twrite_data(void *ptr, size_t size, size_t nmemb, FILE *stream) {return fwrite(ptr, size, nmemb, stream);}intdownload_with_resume(constchar *url, constchar *output_file) { CURL *curl; FILE *fp; CURLcode res;long file_size = 0;structstatst;if (stat(output_file, &st) == 0) { file_size = st.st_size; } curl = curl_easy_init();if (!curl) {return-1; } fp = fopen(output_file, "ab");if (!fp) { curl_easy_cleanup(curl);return-1; } curl_easy_setopt(curl, CURLOPT_URL, url); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_data); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); curl_easy_setopt(curl, CURLOPT_RESUME_FROM, file_size); curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 30L); curl_easy_setopt(curl, CURLOPT_TIMEOUT, 600L); res = curl_easy_perform(curl); fclose(fp); curl_easy_cleanup(curl);return (res == CURLE_OK) ? 0 : -1;}intmain() {constchar *url = "http://example.com/firmware.bin";constchar *output = "firmware.bin"; curl_global_init(CURL_GLOBAL_DEFAULT);if (download_with_resume(url, output) == 0) {printf("下载完成\n"); } else {printf("下载失败\n"); } curl_global_cleanup();return0;}编译命令:
gcc -o download download.c -lcurlAPI说明:
• CURLOPT_RESUME_FROM:设置续传起始位置• CURLOPT_CONNECTTIMEOUT:连接超时时间• CURLOPT_TIMEOUT:总传输超时时间
夜雨聆风