PHP stream_socket_pair 怎么让父子进程双向传递消息
先用 stream_socket_pair 创建两个相连的本地socket流,再调用 pcntl_fork。父进程保留端点0、关闭端点1,子进程保留端点1、关闭端点0;两边保留的端点都能读写。再约定父进程先发请求、子进程读完后回复,就能完成一次双向通信。
官方地址:https://www.php.net/manual/en/function.stream-socket-pair.php
先确认是CLI父子进程通信
遇到“父进程发了消息,子进程没有反应”时,我会先看运行环境和协议约定,而不是先加入sleep。本文采用Unix类系统CLI、PCNTL和阻塞流的小消息场景;代码使用PHP 8字符串函数,不适合作为Windows父子进程方案直接照搬,也不应直接放进普通Web请求处理流程。
这里没有监听端口、接受连接或连接远端服务器。pair在fork之前创建,父子进程继承连接资源。若各自在fork之后创建一对socket,得到的是两套独立连接,不能据相同数组下标建立通信。
把两个端点分别交给父进程与子进程
两个端点是对等的,下标只用于本例分工,不是“读端”和“写端”。父端写出的数据由子端读取,子端写出的数据由父端读取。fork以后双方都会暂时持有两个端点,应立即关闭各自不用的那个。
关闭多余端点有两个用途:减少资源占用,让连接的结束语义更明确。仍有进程持有某个端点时,另一侧等待EOF可能与预期不同。不能只在PHP变量层面说“不使用”,而不实际关闭资源。

用换行协议完成一次请求和应答
流式socket承载字节,不为每次fwrite保存应用消息边界。下面用换行分隔,限制单条消息,并循环处理短写。示例只发送短文本,预期应答内容为 ack:task-42,这不是本文实际运行输出。
4096) {
throw new RuntimeException('消息格式不符合约定');
}
$wire = $message . "\n";
$offset = 0;
while ($offset getMessage() . PHP_EOL);
$exitCode = 1;
} finally {
fclose($child);
}
exit($exitCode); // 子进程不继续执行父进程分支。
}
fclose($pair[1]); // 父进程只保留端点0,同一端点可以读也可以写。
$parent = $pair[0];
$reply = null;
$failure = null;
try {
if (!stream_set_timeout($parent, 5)) {
throw new RuntimeException('设置读取超时失败');
}
sendLine($parent, 'task-42'); // 父进程先写,避免双方都等待读取。
$reply = receiveLine($parent);
} catch (Throwable $e) {
$failure = $e;
} finally {
fclose($parent);
// 关闭端点后回收子进程;信号中断时继续等待。
do {
$waited = pcntl_waitpid($pid, $status);
} while ($waited === -1 && pcntl_get_last_error() === PCNTL_EINTR);
}
if ($failure !== null) {
throw $failure;
}
if ($waited !== $pid || !pcntl_wifexited($status)
|| pcntl_wexitstatus($status) !== 0) {
throw new RuntimeException('子进程未正常结束');
}
echo $reply, PHP_EOL;
这里把数据写入、读到完整应答、子进程正常退出分别判断。写入成功只表示字节交给流,不表示对方已经处理;收到应答才体现这一轮协议完成。
示例的4096字节限制是教学选择。子进程在载荷前添加 ack:,所以接近长度上限的请求可能使应答超限并进入错误路径;业务设计应同时计算请求和应答的最大尺寸。任意二进制或多行消息更适合长度前缀协议,本例不扩展这部分。
卡住或读到半条消息时检查什么
| 现象 | 先检查 | 对应处理 |
|---|---|---|
| 双方都停在读取 | 是否双方都先fgets | 指定一方先发送 |
| 消息有内容却不返回 | 是否缺少换行 | 保证消息结束符完整 |
| 只传了部分内容 | fwrite返回的字节数 | 继续写剩余字节,0或false进入错误路径 |
| 读到片段却误认为成功 | 是否确实有完整结束符 | 拒绝不完整帧,不当正常消息使用 |
| 等待EOF没有结束 | 多余端点是否仍被持有 | 关闭每个进程不用的端点 |
五秒超时只用于帮助这个短消息示例识别读取异常,不是整个父子进程生命周期的硬截止时间。实际应用若子进程可能陷入业务计算,需要额外的生命周期管理;父进程的waitpid不会因为流的读取超时自动结束等待。
双方先写大量数据也可能因缓冲区容量而阻塞。这个请求应答示例没有实现大消息并发收发,也没有非阻塞事件循环;不能只把字符串放大就认为能保持同样行为。

把端点关闭与子进程回收一起收尾
子进程需要在自己的分支关闭端点并退出,父进程关闭端点后还要回收子进程。通信资源的关闭与进程状态回收属于不同工作:fclose不能替代waitpid,waitpid也不能替代消息是否完整的判断。
我会保留四个最小约定:端点归属固定、消息边界明确、读写顺序一致、异常后能够关闭和回收。它们能让短消息通信的行为更容易解释,比依赖任意休眠时间更可靠。
一定要两对socket才能双向通信吗?不用。一对相连的流式socket两侧都能读写。
收到EOF算一条成功消息吗?本例不算。只有带换行的完整消息才成功,EOF前的残缺内容进入错误路径。
可以先waitpid再读应答吗?不适合盲目这样做。子进程可能等待父进程读取才能完成写入,父进程却在等待它退出,形成互等。
参考:stream_socket_pair;pcntl_fork;fwrite;fgets;pcntl_waitpid。
OpenTelemetry Kubernetes 事件怎样关联到发布批次
- 上一篇
- OpenTelemetry Kubernetes 事件怎样关联到发布批次
- 下一篇
- CNCF 云原生 AI 应用怎样划分平台与业务边界
-
- 文章 · php教程 | 7小时前 | 错误处理 · php教程 · php stream_filter_append stream_filter_remove zlib.deflate 流过滤器
- PHP stream_filter_remove 怎样只移除输出流上的压缩过滤器
- 381浏览 收藏
-
- 文章 · php教程 | 7小时前 |
- PHP DatePeriod 的 recurrences 为什么不等于实际日期总数
- 239浏览 收藏
-
- 文章 · php教程 | 8小时前 |
- PHP IntlDateFormatter 严格解析非法日期的 setLenient 配置方法
- 129浏览 收藏
-
- 文章 · php教程 | 8小时前 | PHP · php 关联数组 数组键 内部指针 array_key_last
- PHP array_key_last 怎样取最后一个键而不移动内部指针
- 371浏览 收藏
-
- 文章 · php教程 | 21小时前 |
- PHP PDO::FETCH_KEY_PAIR怎样把两列结果映射成键值表
- 467浏览 收藏
-
- 文章 · php教程 | 22小时前 |
- PHP PDO::FETCH_GROUP怎样把查询结果按字段分组
- 436浏览 收藏
-
- 文章 · php教程 | 22小时前 |
- PHP 8.5 clone with怎样复制对象并替换少量属性
- 106浏览 收藏
-
- 文章 · php教程 | 22小时前 |
- PHP Uri\Rfc3986怎样把 URL 规范化结果用于缓存键
- 207浏览 收藏
-
- 文章 · php教程 | 22小时前 |
- PHP 8.5 pipe operator怎样把连续转换写成可读链式表达
- 316浏览 收藏
-
- 文章 · php教程 | 22小时前 |
- PHP Uri\WhatWg怎样处理浏览器风格的相对 URL
- 210浏览 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 485次学习
-
- PubMedQA
- 深入了解PubMedQA生物医学问答数据集,涵盖其核心功能、使用方法及在临床决策、药物研发等场景的应用,助力提升NLP模型性能。
- 893次使用
-
- H2O EvalGPT
- H2O EvalGPT是H2O.ai推出的开源LLM评估平台,提供详细的大模型性能排行榜、行业特定基准测试及A/B测试功能,助您快速选择最适合项目的高性能大语言模型。
- 895次使用
-
- LMArena
- LMArena是加州大学伯克利分校推出的AI模型匿名评测平台。通过盲测投票机制,用户可对比不同大模型回答并生成实时排行榜,助力开发者优化模型及用户选择最佳AI工具。
- 979次使用
-
- HELM
- 深入了解斯坦福推出的HELM(Holistic Evaluation of Language Models)大模型评测体系。本文解析其核心功能、安装配置步骤及应用场景,涵盖准确性、公平性、鲁棒性等多维度指标,助力开发者全面优化语言模型性能。
- 968次使用
-
- MMBench
- MMBench是由上海人工智能实验室等机构联合推出的多模态基准测试平台,提供细粒度能力评估、大规模数据集及VLMEvalKit工具。本文详细介绍其核心功能、安装使用方法及应用场景,助力开发者全面评估多模态模型性能。
- 793次使用
-
- PHP fgetcsv escape 参数显式设置与 RFC 4180 兼容处理
- 2026-10-26 501浏览
-
- PHP JSON_THROW_ON_ERROR 抛错后怎么保留原始字段位置
- 2026-09-09 501浏览
-
- PHP 8.5 array_last() 怎么处理空数组:从 null 结果到兼容旧版本的 Polyfill
- 2026-08-16 501浏览
-
- 宝塔配置Ruby环境:RVM+Nginx反代教程
- 2026-05-29 501浏览
-
- unset函数作用范围详解
- 2026-05-29 501浏览

