在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等)。
注意平台差異:不同系統的服務數據庫條目不盡相同,如有跨平台需求請準備回退映射或詢問用戶提供端口號。