TCP断点续传


当然可以,以下是一篇以“TCP 断点续传原理与实战详解”为主题的技术文章,适合发布在博客、CSDN 或公众号中,已配有结构清晰的标题、示意图描述和代码讲解:


🚀TCP 断点续传原理与实战详解:用 C 语言实现文件秒级续传!

在网络不稳定或传输中断的环境中,**大文件传输如果每次都从头开始,是一场灾难。**这时,TCP 断点续传技术就登场了!

本文将带你深入了解 TCP 断点续传的原理,并基于 C 语言实现一个支持秒级续传的上传系统,包括客户端和服务器完整代码与讲解。


🧠 一、什么是 TCP 断点续传?

断点续传是指在文件传输中,因意外中断(如断网、断电)后,能够从上次中断的位置继续传输,避免重复上传或下载已完成部分。

虽然 TCP 协议本身具备可靠性与有序性,但它并不记录传输进度。因此我们需要在应用层设计机制来实现续传功能。


📦 二、断点续传的关键原理

实现续传需解决两个核心问题:

  1. 服务器已接收了多少数据?
  2. 客户端如何从该位置继续发送?

于是,双方就需要通过元信息交换 + 文件偏移定位来配合完成。


🔧 三、项目结构与功能说明

我们将构建一个基于 TCP 的上传客户端接收服务器,支持以下功能:

  • 上传文件
  • 中断重连后继续上传未完成部分
  • 多客户端并发上传(服务端使用多线程)

🧪 四、完整代码实现与讲解

✅ 1. 客户端代码(client.c

// 省略头文件定义
#define SERVER_IP "127.0.0.1"
#define PORT 9000
#define BUF_SIZE 4096

int main(int argc, char *argv[]) {
    if (argc != 2) {
        printf("用法: %s <上传文件名>\n", argv[0]);
        return 1;
    }

    const char *filename = argv[1];
    int fd = open(filename, O_RDONLY);
    if (fd < 0) {
        perror("打开文件失败");
        return 1;
    }

    struct stat st;
    fstat(fd, &st);
    long total_size = st.st_size;

    // 建立 TCP 连接
    int sock = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in serv_addr = {0};
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &serv_addr.sin_addr);
    connect(sock, (struct sockaddr*)&serv_addr, sizeof(serv_addr));

    // 发送文件元信息(文件名:偏移0)
    char meta[1024];
    snprintf(meta, sizeof(meta), "%s:0", filename);
    write(sock, meta, strlen(meta));

    // 接收服务器的 offset 信息
    long offset = 0;
    read(sock, &offset, sizeof(offset));
    printf("服务器已有 %ld 字节,开始续传...\n", offset);

    // 定位文件读取位置
    lseek(fd, offset, SEEK_SET);

    // 传输数据
    char buf[BUF_SIZE];
    ssize_t bytes;
    while ((bytes = read(fd, buf, BUF_SIZE)) > 0) {
        write(sock, buf, bytes);
    }

    printf("上传完成: %s (%ld 字节)\n", filename, total_size);
    close(fd);
    close(sock);
    return 0;
}

💡客户端通过文件名和“0”作为初始偏移,告诉服务器它准备上传一个新文件;服务器再回复已接收的长度,客户端从该位置继续发送。


✅ 2. 服务端代码(server.c

// 省略头文件定义
#define PORT 9000
#define BUF_SIZE 4096

long get_file_size(const char *filename) {
    struct stat st;
    return (stat(filename, &st) == 0) ? st.st_size : 0;
}

void* handle_client(void* arg) {
    int client_fd = (intptr_t)arg;
    char meta[1024] = {0};

    // 接收文件名元信息
    if (read(client_fd, meta, sizeof(meta)) <= 0) {
        close(client_fd);
        return NULL;
    }

    char filename[512];
    long offset = 0;
    sscanf(meta, "%[^:]:%ld", filename, &offset);

    long existing_size = get_file_size(filename);
    write(client_fd, &existing_size, sizeof(existing_size));

    // 以追加方式打开文件
    int fd = open(filename, O_WRONLY | O_CREAT, 0666);
    lseek(fd, existing_size, SEEK_SET);

    // 接收并写入数据
    char buf[BUF_SIZE];
    ssize_t bytes;
    while ((bytes = read(client_fd, buf, BUF_SIZE)) > 0) {
        write(fd, buf, bytes);
    }

    printf("接收完成: %s (共 %ld 字节)\n", filename, get_file_size(filename));
    close(fd);
    close(client_fd);
    return NULL;
}

int main() {
    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
    struct sockaddr_in addr = {0};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(PORT);
    addr.sin_addr.s_addr = INADDR_ANY;

    bind(server_fd, (struct sockaddr*)&addr, sizeof(addr));
    listen(server_fd, 10);
    printf("服务器监听端口 %d...\n", PORT);

    // 多线程处理多个客户端
    while (1) {
        int client_fd = accept(server_fd, NULL, NULL);
        pthread_t tid;
        pthread_create(&tid, NULL, handle_client, (void*)(intptr_t)client_fd);
        pthread_detach(tid);
    }

    close(server_fd);
    return 0;
}

💡服务器端通过 stat() 获取当前文件大小,并告知客户端续传的起点,同时多线程支持并发上传。


🖼️ 五、工作流程图

          客户端                           服务器端
      ┌────────────┐                ┌────────────────┐
      │ 打开文件    │                │ 等待连接        │
      └────┬───────┘                └────┬───────────┘
           │                                │
           ├── 发送文件名:0 ──────────────▶
           │                                │
           │   ◀── 获取文件当前大小(offset)
           │                                │
     从 offset 位置读取并写入 socket         │
           ├──────── 数据传输 ───────────▶
           │                                │
           └────── 上传完成 ─────────────┘

🧱 六、延伸与优化方向

  1. 协议标准化:采用 JSON 或 TLV 协议封装元信息,增强扩展性。
  2. 传输完整性校验:传输完后进行 hash 校验(如 MD5、CRC32)。
  3. 加密压缩传输:结合 zlib、OpenSSL 提升安全性与效率。
  4. 断点存储持久化:中断时将 offset 写入文件或数据库,下次读取。
  5. 并发控制与线程池:避免服务器线程数无限增长。

📌 七、总结

TCP 断点续传虽然不是 TCP 自带的功能,但我们可以在应用层借助 offset 实现断点续传逻辑。本项目是其最简实战版,涵盖:

  • 客户端断点上传逻辑
  • 服务器端接收与偏移判断
  • 多线程支持并发连接
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

花落已飘

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值