Workerman发送大文件需分包处理:单次不超过64KB,用fopen+fread流式读取;加4字节长度头协议;通过onBufferFull/onBufferDrain控制缓冲区;接收端按长度头精确接收并流式写盘。

Workerman发送大文件或大数据包时,若直接用send()一次性传完整个二进制内容,极易触发内存暴涨、Worker进程卡死甚至OOM崩溃——因为TCP底层不会自动分片,而Workerman默认不拆包。
发送前必须分包
第一步:确定单次发送上限。建议不超过64KB(65536字节),这是Linux内核socket缓冲区的常见安全阈值,超过易被截断或阻塞。
第二步:读取文件流,按块切分。不要用file_get_contents()加载整文件到内存,改用fopen()+fread()循环读取:
$fp = fopen($filepath, 'rb'); while (!feof($fp)) { $chunk = fread($fp, 65536); if ($chunk === false) break; $connection->send($chunk); }
这一步必须做,否则100MB文件会瞬间吃光Worker进程全部可用内存。
发送端加长度头协议
方法一:固定4字节长度头(推荐)
在每块数据前拼接一个4字节网络字节序的长度标识:$len = pack('N', strlen($chunk)); $connection->send($len . $chunk);
【接收端必须按此协议解析,否则无法识别边界】
方法二:用换行符分隔(仅限纯文本)
每块末尾加\n,但二进制文件含\n会导致误切,不适用于图片、ZIP等任意字节流。
避免阻塞式等待确认
Workerman默认不提供发送ACK机制。若业务要求可靠传输,不能写while (true) { $connection->send(...); sleep(1); }——这会让事件循环停摆,其他连接全部挂起。
正确做法是绑定onBufferFull回调,在发送缓冲区满时暂停推送,等onBufferDrain触发后再续发:
$connection->onBufferFull = function($connection) { $connection->pause(); }; $connection->onBufferDrain = function($connection) { $connection->resume(); };
这一步漏掉,高并发下多个大文件同时发送,必然导致部分连接永久卡在缓冲区满状态。
客户端接收端同步改造
① 接收端先读4字节长度头,用unpack('Nlen', $raw)解析出后续数据长度;
② 再循环recv()直到凑够该长度,严禁直接recv(65536)硬截;
③ 每收到一块就立即写入临时文件,而非拼接成大字符串再存盘——否则PHP字符串内存开销翻倍。
若跳过第②步,TCP粘包会使后续所有数据错位,解包完全失败。


















