一、先搞懂传统文件传输为什么慢先看我们平时写的普通文件传输代码socket 发送文件// 伪代码传统 read write 传输char buf[4096];read(fd, buf, 4096); // 1. 内核→用户态拷贝文件到缓冲区write(sock, buf, 4096);// 2. 用户态→内核拷贝缓冲区到socket传统传输的 4 次拷贝 2 次上下文切换硬盘 → 内核缓冲区DMA拷贝内核缓冲区 → 用户缓冲区CPU拷贝用户缓冲区 → Socket内核缓冲区CPU拷贝Socket内核缓冲区 → 网卡DMA拷贝问题CPU 干了两次无用功上下文切换频繁大文件传输极慢。二、sendfile 核心原理零拷贝sendfile 直接让内核在内核态完成文件到 socket 的传输完全不经过用户态sendfile 只有 2 次拷贝 0 次用户态切换硬盘 → 内核文件缓冲区DMA内核文件缓冲区 → Socket缓冲区DMA/硬件优化直接发给网卡一句话总结sendfile 让内核直接把文件数据扔给网卡用户程序不参与数据拷贝实现零拷贝。三、sendfile 函数原型这是你写代码必须记住的标准格式#include sys/sendfile.hssize_t sendfile(int out_fd, // 输出文件描述符必须是 socketint in_fd, // 输入文件描述符必须是普通文件/磁盘文件off_t *offset, // 文件读取起始偏移量传 NULL 表示从当前位置读size_t count // 要传输的字节数);返回值必须会成功返回实际传输的字节数失败返回 -1设置 errno四、sendfile 硬性限制sendfile 不是万能的有严格限制必须记死1.out_fd 必须是 socket 描述符不能是普通文件、管道、终端只能用于网络发送文件2.in_fd 必须是普通磁盘文件不能是 socket、管道、内存文件必须支持 seek 寻址普通文件都支持3.无法修改数据内核直接转发你不能在传输时加密、压缩、修改内容纯转发场景才用它4.Linux 专属Windows、macOS 有类似函数但不通用跨平台程序不能只靠 sendfile五、最简可运行代码这是最标准的 sendfile 服务端代码#include stdio.h#include sys/sendfile.h#include sys/stat.h#include fcntl.h#include unistd.h#include sys/socket.hint main() {// 1. 打开要发送的文件int file_fd open(test.txt, O_RDONLY);struct stat st;fstat(file_fd, st); // 获取文件大小// 2. 假设已经建立好 socket 连接conn_fd 是客户端socketint conn_fd accept(...); // 省略socket创建、绑定、监听// 3. sendfile 零拷贝传输off_t offset 0; ssize_t ret sendfile(conn_fd, file_fd, offset, st.st_size);if (ret 0) {perror(sendfile failed);} else {printf(成功传输 %zd 字节\n, ret);}close(file_fd);close(conn_fd);return 0;}六、必须会的核心知识点1. 什么是零拷贝零拷贝 避免 CPU 在用户态和内核态之间拷贝数据 sendfile 是 Linux 最经典的零拷贝实现。2. sendfile 优势速度极快大文件提升 30%~200%减少 CPU 占用代码极简内核级优化稳定可靠3. 适用场景Web 服务器发送静态文件html、css、js、图片文件服务器下载纯转发、不修改数据的网络传输4. 不适用场景需要加密/压缩文件跨平台程序传输非磁盘文件socket→socket 不行5. 与 mmap 的区别必考题sendfile简单、专用、零拷贝、不能改数据mmap映射文件到内存、可以修改数据、复杂七、一句话总结最核心sendfile 内核态零拷贝 文件直接发socket 不经过用户态 高性能 只能转发磁盘文件总结原理内核直接转发文件到 socket零拷贝、无用户态切换函数sendfile(out_socket, in_file, offset, size)限制输出必须是 socket输入必须是普通文件用途Web服务器、文件下载、高性能网络转发优势比 read/write 快得多CPU 占用低参考链接 0voice · GitHub