在 PHP 中,getservbyname(string $service, string $protocol) 可以根据服务名称(如 http、ssh)和协议(tcp 或 udp)返回对应的端口号(十进制整数)。如果找不到该服务名或发生错误,函数会返回 false。因此我们可以用它来判断某个服务名称是否在系统服务数据库(例如 Linux 下的 /etc/services、Windows 的 services 文件)中存在 —— 注意:这是查名字是否有映射到端口,而不是检测该服务当前是否可连接或可用。
下面示例展示常见用法、封装函数、注意事项与实际可用性的额外检测方法(如果你需要确认服务是否真的能连通)。
<span><span><span class="hljs-meta"><?php</span></span><span>
</span><span><span class="hljs-variable">$service</span></span><span> = </span><span><span class="hljs-string">'http'</span></span><span>;
</span><span><span class="hljs-variable">$protocol</span></span><span> = </span><span><span class="hljs-string">'tcp'</span></span><span>;
</span><span><span class="hljs-variable">$port</span></span><span> = </span><span><span class="hljs-title function_ invoke__">getservbyname</span></span><span>(</span><span><span class="hljs-variable">$service</span></span><span>, </span><span><span class="hljs-variable">$protocol</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$port</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"服务名 '<span class="hljs-subst">{$service}</span></span></span><span>'(协议 </span><span><span class="hljs-subst">{$protocol}</span></span><span>)在本机服务数据库中不存在。\n";
} </span><span><span class="hljs-keyword">else</span></span><span> {
</span><span><span class="hljs-keyword">echo</span></span><span> </span><span><span class="hljs-string">"服务 '<span class="hljs-subst">{$service}</span></span></span><span>'(</span><span><span class="hljs-subst">{$protocol}</span></span><span>)映射到端口:</span><span><span class="hljs-subst">{$port}</span></span><span>\n";
}
</span></span>
这个函数会更稳健地判断:既检查 getservbyname 返回值,也确保端口号在合法范围(1–65535)内:
<span><span><span class="hljs-meta"><?php</span></span><span>
<span class="hljs-comment">/**
* 判断服务名称是否有效(在本机服务数据库中有对应端口)
*
* </span></span><span><span class="hljs-doctag">@param</span></span><span> string $service 服务名称,例如 'http'、'ssh'
* </span><span><span class="hljs-doctag">@param</span></span><span> string $protocol 协议 'tcp' 或 'udp'
* </span><span><span class="hljs-doctag">@return</span></span><span> bool 返回 true 表示有效(找到合法端口),false 表示无效或出错
*/
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">is_service_name_valid</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-keyword">string</span></span></span><span> </span><span><span class="hljs-variable">$service</span></span><span>, </span><span><span class="hljs-keyword">string</span></span><span> </span><span><span class="hljs-variable">$protocol</span></span><span> = </span><span><span class="hljs-string">'tcp'</span></span><span>): </span><span><span class="hljs-title">bool</span></span><span>
{
</span><span><span class="hljs-comment">// 规范化参数</span></span><span>
</span><span><span class="hljs-variable">$protocol</span></span><span> = </span><span><span class="hljs-title function_ invoke__">strtolower</span></span><span>(</span><span><span class="hljs-variable">$protocol</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (!</span><span><span class="hljs-title function_ invoke__">in_array</span></span><span>(</span><span><span class="hljs-variable">$protocol</span></span><span>, [</span><span><span class="hljs-string">'tcp'</span></span><span>, </span><span><span class="hljs-string">'udp'</span></span><span>], </span><span><span class="hljs-literal">true</span></span><span>)) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-literal">false</span></span><span>;
}
</span><span><span class="hljs-variable">$port</span></span><span> = @</span><span><span class="hljs-title function_ invoke__">getservbyname</span></span><span>(</span><span><span class="hljs-variable">$service</span></span><span>, </span><span><span class="hljs-variable">$protocol</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$port</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-literal">false</span></span><span>;
}
</span><span><span class="hljs-comment">// 确保端口在合法范围</span></span><span>
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-title function_ invoke__">is_int</span></span><span>(</span><span><span class="hljs-variable">$port</span></span><span>) && </span><span><span class="hljs-variable">$port</span></span><span> >= </span><span><span class="hljs-number">1</span></span><span> && </span><span><span class="hljs-variable">$port</span></span><span> <= </span><span><span class="hljs-number">65535</span></span><span>;
}
</span><span><span class="hljs-comment">// 示例</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">is_service_name_valid</span></span><span>(</span><span><span class="hljs-string">'http'</span></span><span>, </span><span><span class="hljs-string">'tcp'</span></span><span>)); </span><span><span class="hljs-comment">// 通常 true</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">is_service_name_valid</span></span><span>(</span><span><span class="hljs-string">'nosuchservice'</span></span><span>, </span><span><span class="hljs-string">'tcp'</span></span><span>)); </span><span><span class="hljs-comment">// false</span></span><span>
</span></span>
getservbyname 查询的是 操作系统的服务数据库(如 /etc/services)。不同系统、不同安装环境、不同发行版可能含有不同条目。例如某些自定义服务在一个主机上存在而在另一个主机上不存在。
该函数只告诉你服务名是否有端口映射 —— 并不表示该端口上的服务当前正在运行或可连通。如果你需要检查“服务可用性”,还需要执行网络连接测试(例如 fsockopen、stream_socket_client 或 socket_create/socket_connect)。
协议必须是 'tcp' 或 'udp'(大小写不敏感)。其他值会被视为无效。
判断服务名存在只是第一步。如果你想检查远程主机(或本地主机)是否在该服务端口上接受连接,可以先用 getservbyname 取端口,然后尝试建立连接。
<span><span><span class="hljs-meta"><?php</span></span><span>
<span class="hljs-comment">/**
* 先用 getservbyname 判断服务名,再尝试连接目标主机的该端口以确认可达性
*
* </span></span><span><span class="hljs-doctag">@param</span></span><span> string $host 目标主机(IP 或 主机名)
* </span><span><span class="hljs-doctag">@param</span></span><span> string $service 服务名称
* </span><span><span class="hljs-doctag">@param</span></span><span> string $protocol 'tcp' 或 'udp'(这里只实现 tcp 的连接检测)
* </span><span><span class="hljs-doctag">@param</span></span><span> float $timeout 秒(例如 2.0)
* </span><span><span class="hljs-doctag">@return</span></span><span> bool true 表示找到端口且能建立 TCP 连接,false 否则
*/
</span><span><span class="hljs-function"><span class="hljs-keyword">function</span></span></span><span> </span><span><span class="hljs-title">is_service_reachable</span></span><span>(</span><span><span class="hljs-params"><span class="hljs-keyword">string</span></span></span><span> </span><span><span class="hljs-variable">$host</span></span><span>, </span><span><span class="hljs-keyword">string</span></span><span> </span><span><span class="hljs-variable">$service</span></span><span>, </span><span><span class="hljs-keyword">string</span></span><span> </span><span><span class="hljs-variable">$protocol</span></span><span> = </span><span><span class="hljs-string">'tcp'</span></span><span>, </span><span><span class="hljs-keyword">float</span></span><span> </span><span><span class="hljs-variable">$timeout</span></span><span> = </span><span><span class="hljs-number">2.0</span></span><span>): </span><span><span class="hljs-title">bool</span></span><span>
{
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-title function_ invoke__">strtolower</span></span><span>(</span><span><span class="hljs-variable">$protocol</span></span><span>) !== </span><span><span class="hljs-string">'tcp'</span></span><span>) {
</span><span><span class="hljs-comment">// 简化:这里只做 TCP 的连通性检测;UDP 检测较复杂(无连接/无确认)</span></span><span>
</span><span><span class="hljs-keyword">throw</span></span><span> </span><span><span class="hljs-keyword">new</span></span><span> </span><span><span class="hljs-built_in">InvalidArgumentException</span></span><span>(</span><span><span class="hljs-string">'is_service_reachable 目前只支持 tcp 检测'</span></span><span>);
}
</span><span><span class="hljs-variable">$port</span></span><span> = @</span><span><span class="hljs-title function_ invoke__">getservbyname</span></span><span>(</span><span><span class="hljs-variable">$service</span></span><span>, </span><span><span class="hljs-variable">$protocol</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$port</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span> || !</span><span><span class="hljs-title function_ invoke__">is_int</span></span><span>(</span><span><span class="hljs-variable">$port</span></span><span>) || </span><span><span class="hljs-variable">$port</span></span><span> < </span><span><span class="hljs-number">1</span></span><span> || </span><span><span class="hljs-variable">$port</span></span><span> > </span><span><span class="hljs-number">65535</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-literal">false</span></span><span>;
}
</span><span><span class="hljs-variable">$errno</span></span><span> = </span><span><span class="hljs-number">0</span></span><span>;
</span><span><span class="hljs-variable">$errstr</span></span><span> = </span><span><span class="hljs-string">''</span></span><span>;
</span><span><span class="hljs-variable">$fp</span></span><span> = @</span><span><span class="hljs-title function_ invoke__">fsockopen</span></span><span>(</span><span><span class="hljs-variable">$host</span></span><span>, </span><span><span class="hljs-variable">$port</span></span><span>, </span><span><span class="hljs-variable">$errno</span></span><span>, </span><span><span class="hljs-variable">$errstr</span></span><span>, </span><span><span class="hljs-variable">$timeout</span></span><span>);
</span><span><span class="hljs-keyword">if</span></span><span> (</span><span><span class="hljs-variable">$fp</span></span><span> === </span><span><span class="hljs-literal">false</span></span><span>) {
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-literal">false</span></span><span>;
}
</span><span><span class="hljs-title function_ invoke__">fclose</span></span><span>(</span><span><span class="hljs-variable">$fp</span></span><span>);
</span><span><span class="hljs-keyword">return</span></span><span> </span><span><span class="hljs-literal">true</span></span><span>;
}
</span><span><span class="hljs-comment">// 示例</span></span><span>
</span><span><span class="hljs-title function_ invoke__">var_dump</span></span><span>(</span><span><span class="hljs-title function_ invoke__">is_service_reachable</span></span><span>(</span><span><span class="hljs-string">'127.0.0.1'</span></span><span>, </span><span><span class="hljs-string">'ssh'</span></span><span>, </span><span><span class="hljs-string">'tcp'</span></span><span>, </span><span><span class="hljs-number">1.5</span></span><span>)); </span><span><span class="hljs-comment">// 本机 ssh 服务是否可达</span></span><span>
</span></span>
注意:UDP 的“可达性”判断不是简单的 connect:通常需要发送探测包并等待响应,或使用更复杂的 socket API(并且可能被防火墙丢弃)。如果确实需要 UDP 检测,要实现应用层探测逻辑或采用专门工具。
配置验证:当用户在配置界面输入服务名(例如 smtp、imap),先用 getservbyname 验证名字是否被系统识别。如果未识别,可提示用户使用端口号或选择其他服务名。
兼容性:不要硬依赖系统的服务数据库——在需要跨平台一致性时,维护一个应用内映射表(如 'http' => 80)作为回退。
权限与安全:尝试连接端口进行可达性检测时,注意网络策略、防火墙和速率限制,不要在短时间内对大量主机频繁探测以免造成滥用或触发安全告警。
错误处理:getservbyname 可能返回 false,请在代码中显式处理,而不是直接使用返回值。
使用 getservbyname($service, $protocol) 可以判断服务名称在本机系统服务数据库中是否有端口映射(返回端口号或 false)。
判断有效性时最好检查返回值类型并确保端口在 1–65535 范围内。
该方法不代表服务可用;若需确认可用性,还需进一步做网络连接层面的检测(如 fsockopen 等)。
注意平台差异:不同系统的服务数据库条目不尽相同,如有跨平台需求请准备回退映射或询问用户提供端口号。