摘要

  • 难题很实际:服务重启后可以换一个空闲端口,客户端却不能靠猜找到新入口;如果每个程序都预留固定端口,协调负担就落回共享端口空间。
  • RFC 1057 的 Portmapper 用固定的本机查询入口解决“端口变了,客户端怎么知道”。它只返回登记位置,不证明服务在线、网络可达或对端身份。

服务端口可以变,查询入口保持固定

设想一个假设场景:文件服务重启后改用另一个空闲端口,客户端仍连接旧端口,于是调用无法到达。主机和 RPC 程序都没变,只有服务实例的位置变了;客户端缺少的正是更新后的目的地。固定端口看似省掉这一步,却会要求网络为不断变化的程序持续协调编号。

Sun Microsystems 于 1988 年 6 月发布的 RFC 1057 记录了另一种安排:程序号和版本说明客户端想调用什么,TCP 或 UDP 说明使用哪种传输。运行在主机本地、通过众所周知的 111 端口访问的 Portmapper,把这些标识关联到服务器选定的端口。客户端先询问映射器,再联系服务。远程调用和端点查询是两个分开的步骤。

PMAPPROC_SET 用于登记。服务器把程序号、版本、传输协议和监听端口交给同一台机器上的映射器。对于相同的程序、版本和协议组合,映射器拒绝重复登记。服务退出时,可以用 PMAPPROC_UNSET 删除记录。客户端调用 PMAPPROC_GETPORT 查询;如果返回零,表示没有该组合的登记项。

职责划分很清楚:服务自行选择监听位置,映射器在固定入口公布这个选择,客户端再使用返回的端口。服务实例可以移动,查询约定仍然稳定。

查询成功并不等于调用成功

映射器给出的答案只反映它本机表中的登记状态。RFC 1057 没有把 GETPORT 定义成端到端健康检查,也没有说它能证明网络路径畅通或端口上的程序身份。客户端还要建立连接、发起 RPC 并判断结果。服务已经停止而记录尚未删除时,查询可以成功,后续连接却失败。若记录不存在,客户端遇到的是服务绑定缺失。

“找到了”容易被理解得太强。一个端口只能说明服务曾被登记在这里监听;它不能保证数据包能到达、服务会接受这个客户端,或目标过程能够完成。RFC 将认证与传输语义分别讨论,Portmapper 不承担这些职责。

这张表也不是全球 RPC 目录,而是服务所在主机的本地记录。客户端还需从其他地方得到主机地址,然后询问该主机上的映射器。于是,发现过程分散在多个层面:名称或配置提供主机,程序号和版本确定接口,Portmapper 给出传输端点。

RFC 1057 还定义了 PMAPPROC_DUMP 来枚举映射项,以及为广播调用设计的 PMAPPROC_CALLIT。客户端把请求发到广播地址上的固定映射端口;收到请求的各台映射器可以调用本机已登记的服务。只有执行成功时映射器才回送结果,否则保持沉默。这使服务端口各不相同时仍可尝试广播调用,但并不保证送达,也不构成所有响应主机的完整清单。

程序编号也有自己的协调范围。RFC 1057 把编号分为 Sun 定义、用户定义、临时和保留区段。逻辑程序身份因此不必与变化的传输端口绑定:一个编号命名 RPC 程序,另一个在本机解析出的值标示当前实例在哪里监听。

从 TCP/UDP 端口映射到通用地址

早期 Portmapper 直接处理 TCP 和 UDP 端口。1995 年的 RFC 1833 同时记录了第二版 Port Mapper,以及第三、第四版 RPCBIND。它说明服务的传输位置可能动态变化,客户端因而需要在服务器地址上通过固定入口查询。RPCBIND 使用通用地址字符串表示传输地址,并带有网络标识,把绑定机制扩展到原有 TCP/UDP 端口形式之外。

后来的文本帮助划清 1988 年设计的边界。RFC 1057 没有把 RPC 程序绑死在某个传输端口,而是定义一种会合机制,把程序和版本映射到特定传输的监听地址。RFC 5531 后来明确把 RPC 消息协议与绑定、会合机制分开描述。

这项设计解决的问题很具体:只要双方采用同一查询约定,客户端就不必预先知道服务端口。代价是调用前增加一次查询,也要维护与服务启动、登记、退出和注销同步的状态。

这些 RFC 记录的是设计机制,不是历史部署规模或可靠性统计。它们没有说明当年出现过多少过期登记,也无法衡量具体系统修复状态的速度。可以确定的结论是架构层面的:Portmapper 通过稳定的本机入口让 RPC 端点可查;网络可达性、认证和调用是否成功仍需后续验证。

一手文件:RFC 1057 — RPC 第二版;RFC 1833 — ONC RPC 绑定协议;RFC 5531 — ONC RPC 第二版。