文章目录
当然可以,以下是一篇以“TCP 断点续传原理与实战详解”为主题的技术文章,适合发布在博客、CSDN 或公众号中,已配有结构清晰的标题、示意图描述和代码讲解:
🚀TCP 断点续传原理与实战详解:用 C 语言实现文件秒级续传!
在网络不稳定或传输中断的环境中,**大文件传输如果每次都从头开始,是一场灾难。**这时,TCP 断点续传技术就登场了!
本文将带你深入了解 TCP 断点续传的原理,并基于 C 语言实现一个支持秒级续传的上传系统,包括客户端和服务器完整代码与讲解。
🧠 一、什么是 TCP 断点续传?
断点续传是指在文件传输中,因意外中断(如断网、断电)后,能够从上次中断的位置继续传输,避免重复上传或下载已完成部分。
虽然 TCP 协议本身具备可靠性与有序性,但它并不记录传输进度。因此我们需要在应用层设计机制来实现续传功能。
📦 二、断点续传的关键原理
实现续传需解决两个核心问题:
- 服务器已接收了多少数据?
- 客户端如何从该位置继续发送?
于是,双方就需要通过元信息交换 + 文件偏移定位来配合完成。
🔧 三、项目结构与功能说明
我们将构建一个基于 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 │
├──────── 数据传输 ───────────▶
│ │
└────── 上传完成 ─────────────┘
🧱 六、延伸与优化方向
- 协议标准化:采用 JSON 或 TLV 协议封装元信息,增强扩展性。
- 传输完整性校验:传输完后进行 hash 校验(如 MD5、CRC32)。
- 加密压缩传输:结合 zlib、OpenSSL 提升安全性与效率。
- 断点存储持久化:中断时将 offset 写入文件或数据库,下次读取。
- 并发控制与线程池:避免服务器线程数无限增长。
📌 七、总结
TCP 断点续传虽然不是 TCP 自带的功能,但我们可以在应用层借助 offset 实现断点续传逻辑。本项目是其最简实战版,涵盖:
- 客户端断点上传逻辑
- 服务器端接收与偏移判断
- 多线程支持并发连接