php 的流传输层可以把 tcp 连接加密成 tls,却始终没有一个官方手段去加密 udp——直到 2026-11-10,wiki.php.net 的 rfc 索引页在「in draft」区新增了一条草案:dtls support in ext/openssl。草案提议在 ext/openssl 中引入 dtls://、dtlsv1.2:// 与 dtlsv1.3:// 三种流传输,让 stream_socket_client()、stream_socket_server() 能像使用 tls:// 一样使用 dtls。当前状态为 draft、版本 0.1,讨论串与投票串均标注 tbd,尚未在 php internals 邮件列表公布。

▲ wiki.php.net RFC 页面
草案要解决的问题:UDP 流量没有原生加密手段
DTLS 是 TLS 针对数据报的改造版本,由 RFC 6347(DTLS 1.2)与 RFC 9147(DTLS 1.3)定义,被大量物联网与实时通信协议当作默认的安全层:CoAP 与 LwM2M 用 5684 端口的 DTLS 保护设备通信,WebRTC 数据通道、SIP、各类远程接入隧道以及加密 syslog 也都建立在它之上。
问题在于 PHP 端一直没有对应能力。现有 ext/openssl 只把 TLS 接到 tcp:// 流上,一旦业务需要与 DTLS 端点对话,开发者只有两条路:起一个外部进程(openssl s_client、stunnel 之类)做转发,或者自己写 C 扩展。两种做法都把复杂度推给了部署环节,也让 PHP 在 CoAP 服务器、WebRTC 信令以外的场景里缺位。草案作者 Gianfrancesco Aurecchia 给出的目标很直接:把这些能力做进内核自带的扩展,用法与 tls:// 保持一致。
新增的传输协议与调用方式
草案计划注册三种传输协议名,区别只在允许协商的 DTLS 版本:dtls:// 同时允许 DTLS 1.2 与 1.3(1.3 需底层 OpenSSL 支持),dtlsv1.2:// 只允许 1.2,dtlsv1.3:// 只允许 1.3 且仅在该 OpenSSL 支持时注册。客户端与服务端都可使用。
<?php $ctx = stream_context_create(['ssl' => ['cafile' => '/etc/ssl/ca.pem']]);
$client = stream_socket_client('dtls://example.com:5684', $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $ctx);
fwrite($client, "ping"); // 一次 fwrite() 对应一条 DTLS 记录
echo fread($client, 1500); // 一次 fread() 返回一个数据报
?>
▲ 草案正文中的客户端示例
与 tls:// 一样,DTLS 也支持「先建明文流、再升级加密」。草案把 udp:// 一并交给 ext/openssl 提供(现状是 tcp:// 由该扩展提供),于是已有的 UDP 流可以用 stream_socket_enable_crypto() 就地升级:
<?php $client = stream_socket_client('udp://example.com:5684', $errno, $errstr, 5, STREAM_CLIENT_CONNECT, $ctx);
stream_socket_enable_crypto($client, true, STREAM_CRYPTO_METHOD_DTLS_ANY_CLIENT);
?>
配套新增六个常量:STREAM_CRYPTO_METHOD_DTLSv1_2_CLIENT、STREAM_CRYPTO_METHOD_DTLSv1_3_CLIENT、STREAM_CRYPTO_METHOD_DTLS_ANY_CLIENT 及对应的三个 _SERVER 版本,用于在 stream_socket_enable_crypto() 与 ssl 上下文的 crypto_method 中显式指定协议族。草案里的常量值目前写作 UNKNOWN,属占位状态。

▲ wiki.php.net RFC 索引页
实现方式:按层拆分,不与 TLS 分叉
草案的 Implementation 字段指向 php/php-src 的 PR #22638,标题为「ext/openssl: add a dtls:// stream transport. Open as Draft.」。该 PR 已迭代 8 次提交,改动 43 个文件、+4719/−605 行,目前仍标记为 Draft。按 PR 描述,实现刻意按「层」而不是按「协议」切分,避免为 DTLS 复制一套选项解析与 IO 循环:
密文传输集中在 xp_bio.c。OpenSSL 自身不做 IO,其 BIO 从一个接收队列取记录、向连接持有的发送队列追加,流在每次 SSL_*() 调用前后把密文搬进搬出。这样任何 OpenSSL 调用都不会阻塞,socket 在加密建立后保持非阻塞,阻塞语义由单调时钟截止时间模拟等待来还原。传输可以是流自身的 socket,也可以是新增的 inner_stream 上下文选项指定的任意流,于是「TLS over 任意流」与「DTLS over 任意流」同时成立。
流实现合并在 xp_ssl.c:数据报协议名只负责挑选 DTLS 方法与版本范围,证书校验、SNI、ALPN、PSK、会话、early data、密码套件、安全等级、元数据等全部与 TLS 共用一套代码,旧的 gettimeofday() 轮询和描述符模式切换被删除。服务端逻辑单独放在 xp_dtls.c:一个 UDP socket 承载所有 peer,按对端地址把数据报路由到各自连接,新 peer 的握手走 cookie 交换(SSL_OP_COOKIE_EXCHANGE、带时间戳的 HMAC cookie、pending 上限与超时),完成握手的连接以共享该 socket 的流形式交给 stream_socket_accept()。值得注意的是它既不用 DTLSv1_listen()(仅支持 1.2、仅单 peer),也不用 OpenSSL 4.1 才有的 listener API,因此从 OpenSSL 1.1.1 起都能工作。

▲ php-src 实现提案 PR
语义与向后兼容性代价
DTLS 在语义上向 tls:// 看齐:verify_peer_name 与 verify_peer 相互独立,peer_fingerprint 在关闭 verify_peer 时依然生效,会话通过 session_new_cb 捕获,握手在 stream_socket_accept() 内部完成。客户端 dtls:// 会协商 1.2 或 1.3;服务端只有在显式指定 crypto_method 时才提供 1.3,因为在 OpenSSL listener 之外,服务端无法在首个 flight 之前验证对端地址。新增的 ssl 上下文选项还包括 dtls_link_mtu、dtls_max_pending(默认 256)、dtls_pending_timeout(默认 30 秒),以及按 RFC 5705 导出密钥材料的 keying_material_label 与 keying_material_length。
兼容性代价写在 Backward Incompatible Changes 一节里,共两处。其一是新增的六个全局常量理论上可能与用户自定义常量重名——作者的检索结论是没有项目定义过 STREAM_CRYPTO_METHOD_DTLS*。其二是更实际的一处:ext/openssl 加载后 udp:// 流由该扩展创建,stream_get_meta_data() 返回的 stream_type 会从 udp_socket 变成 udp_socket/dtls,正如今天的 tcp:// 报告的是 tcp_socket/ssl。作者在 GitHub 上检索到 143 处把 stream_type 与字符串 udp_socket 做严格比较的代码,其中包括 symfony/polyfill,这类比较会因此失效。

▲ RFC 9147 规范页
当前状态与后续观察点
这份文档还远未成型。页面头部标注 Version 0.1、Status Draft,Proposal 主章节下的 Server 与 Examples 两节均为 TBD,Open Issues、Future Scope、Implementation 也还是占位;Voting Choices 里的投票表格显示 0/0/0 且标注 poll closed,属于模板未填,不能解读为已有投票结果。索引页把 In Draft 区定义为「尚未在 PHP internals 邮件列表公布」的早期草案,因此它既没有进入讨论,也没有进入投票。
时间线上有一处需要说明的出入:RFC 页面头部 Date 字段写作 2026-11-12,而索引页条目与 wiki 的 Recent Changes 显示该页创建于 2026-11-10、最后修改于 2026-11-10T21:25:43+00:00。本文按官方页面的实际创建与修改时间记录,不对 Date 字段做外推。若草案继续推进,下一个可观察节点是它从 In Draft 转入 Under Discussion、讨论串与投票串由 tbd 变成真实链接;由于这是主投票,需 2/3 多数通过,目标版本写作「next PHP 8.x」,尚未绑定具体小版本。草案阶段不宜据此做升级或兼容性结论,但把 stream_type 与 udp_socket 做严格比较的代码,值得提前自查一遍。
关键信息
| 项目 | 内容 |
|---|---|
| 类目 | RFC 与需求 |
| 状态 | Draft(Version 0.1,In Draft 区,未公布讨论) |
| 提案人 | Gianfrancesco Aurecchia(gianfri@aurecchia.it) |
| 创建时间 | 2026-11-10(页面 Last modified 2026-11-10T21:25:43+00:00) |
| 新增传输 | dtls://、dtlsv1.2://、dtlsv1.3://(客户端与服务端) |
| 关联编号 | RFC: dtls_support_in_ext_openssl;php/php-src PR #22638 |
| 影响范围 | ext/openssl 流传输层与 main/streams;未绑定具体 PHP 版本 |
| 兼容性影响 | udp:// 流的 stream_type 变为 udp_socket/dtls;新增 6 个 STREAM_CRYPTO_METHOD_DTLS* 常量 |
| 投票门槛 | 主投票需 2/3 多数;讨论串、投票串均为 tbd |
| 核验状态 | 已官方确认(wiki.php.net 页面与 php-src PR 双向核对) |
来源:https://wiki.php.net/rfc/dtls_support_in_ext_openssl
来源:https://wiki.php.net/rfc
来源:https://github.com/php/php-src/pull/22638
来源:https://www.rfc-editor.org/rfc/rfc9147











