send不能直接发文件,只能发内存字符串;sendfile是零拷贝系统调用,专用于静态文件高效传输,支持断点续传但不支持加密或动态处理。

send 不能直接发文件,只能发内存里的字符串
很多人看到 swoole_server->send() 就想拿来传大文件,结果发现要么失败、要么卡死、要么只发了一半。根本原因是:它只接受 string 类型的 $data 参数,底层会把整段数据拷贝进用户态内存再发出去。如果文件是 100MB,你得先 file_get_contents() 把它读进 PHP 内存——这不仅慢,还极易触发 OOM。
常见错误现象:
-
swoole_server->send($fd, file_get_contents($path))在 >2MB 时直接返回false(TCP 协议限制) - 即使成功,也会因频繁内存分配/拷贝拖垮 Worker 进程 CPU
- 无法处理断点续传、Range 请求等 HTTP 下载必需逻辑
sendfile 是零拷贝,路径必须绝对且文件得存在
$response->sendfile($path) 或 $server->sendfile($fd, $path) 调用的是内核 sendfile(2) 系统调用,数据不经过 PHP 用户态,直接从磁盘文件描述符搬运到 socket 描述符。性能差距不是“快一点”,而是“一个在走路一个坐火箭”。
但容易踩的坑很具体:
-
$path必须是绝对路径,__DIR__.'/file.zip'这种拼接后没做realpath()的,遇到符号链接或 chroot 会报No such file or directory - 文件权限要让 Worker 进程可读(注意:不是 Web 服务器用户,而是启动 Swoole 的系统用户)
- 超过 2GB 的文件,在某些旧内核或 32 位环境可能截断,上线前务必用
filesize()校验 - 它不支持加密(如 TLS)、不加 HTTP 头、不做序列化——别指望用它发 JSON 或 Protobuf
HTTP 场景下 sendfile 自动处理 Range 和 206 响应
如果你用的是 Swoole\Http\Response,调用 $response->sendfile($path) 时,Swoole 会自动解析客户端带的 Range 请求头,返回 206 Partial Content、设置 Content-Range 和 Accept-Ranges: bytes。这个能力是 send 完全不具备的。
Swoole 6.1.1 是一个专为 PHP 设计的高性能事件驱动并发网络引擎。作为稳定版,它修复了编译时对 zlib 依赖的缺失及 curl 模块的内存安全风险。该版本支持协程、多线程与多进程架构,内置 TCP/HTTP/WebSocket 服务器,能够显著提升 PHP 在微服务、实时通信等场景下的执行效率与并发能力。
典型使用场景:
- 静态资源服务(CSS/JS/图片)
- 大文件下载(ISO、视频、安装包)
- CDN 回源透传(配合限速、Referer 校验)
但注意:动态生成的文件(比如 ZIP 打包、PDF 渲染结果)不能直接用 sendfile,得走协程分块 write + end 流式发送。
大数据传输选 sendfile,小数据或控制流用 send
send 的真实定位是「连接级控制消息」:登录确认、心跳响应、指令 ACK、JSON-RPC 返回体。它的优势在于低延迟、可精确控制每个 $fd 的发送时机,且 TCP 下原子安全。
sendfile 的边界也很清晰:只适合已落盘的静态内容。一旦涉及权限校验、动态拼接、加解密、压缩、日志埋点等逻辑,就得切回用户态处理——这时候再硬套 sendfile 反而绕远路。
真正复杂的地方在于混合场景:比如一个下载接口既要校验 Token,又要支持断点续传。这时得先做鉴权,再调 sendfile,且确保 header 设置在 sendfile 之前,否则 HTTP 头会丢。










