當前位置: 首頁> 最新文章列表> 如何用PHP 的getservbyname 判斷某個服務名稱是否有效?

如何用PHP 的getservbyname 判斷某個服務名稱是否有效?

gitbox 2025-09-12

在PHP 中, getservbyname(string $service, string $protocol)可以根據服務名稱(如httpssh )和協議( tcpudp )返回對應的端口號(十進制整數)。如果找不到該服務名或發生錯誤,函數會返回false 。因此我們可以用它來判斷某個服務名稱是否在系統服務數據庫(例如Linux 下的/etc/services 、Windows 的services 文件)中存在—— 注意:這是查名字是否有映射到端口,而不是檢測該服務當前是否可連接或可用。

下面示例展示常見用法、封裝函數、注意事項與實際可用性的額外檢測方法(如果你需要確認服務是否真的能連通)。


基本用法

<span><span><span class="hljs-meta">&lt;?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">&lt;?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>) &amp;&amp; </span><span><span class="hljs-variable">$port</span></span><span> &gt;= </span><span><span class="hljs-number">1</span></span><span> &amp;&amp; </span><span><span class="hljs-variable">$port</span></span><span> &lt;= </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 )。不同系統、不同安裝環境、不同發行版可能含有不同條目。例如某些自定義服務在一個主機上存在而在另一個主機上不存在。

  • 該函數只告訴你服務名是否有端口映射—— 並不表示該端口上的服務當前正在運行或可連通。如果你需要檢查“服務可用性”,還需要執行網絡連接測試(例如fsockopenstream_socket_clientsocket_create / socket_connect )。

  • 協議必須是'tcp''udp' (大小寫不敏感)。其他值會被視為無效。

如果你還想確認服務是否可連接(可選)

判斷服務名存在只是第一步。如果你想檢查遠程主機(或本地主機)是否在該服務端口上接受連接,可以先用getservbyname取端口,然後嘗試建立連接。

 <span><span><span class="hljs-meta">&lt;?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> &lt; </span><span><span class="hljs-number">1</span></span><span> || </span><span><span class="hljs-variable">$port</span></span><span> &gt; </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 檢測,要實現應用層探測邏輯或採用專門工具。

常見用例與建議

  1. 配置驗證:當用戶在配置界面輸入服務名(例如smtpimap ),先用getservbyname驗證名字是否被系統識別。如果未識別,可提示用戶使用端口號或選擇其他服務名。

  2. 兼容性:不要硬依賴系統的服務數據庫——在需要跨平台一致性時,維護一個應用內映射表(如'http' => 80 )作為回退。

  3. 權限與安全:嘗試連接端口進行可達性檢測時,注意網絡策略、防火牆和速率限制,不要在短時間內對大量主機頻繁探測以免造成濫用或觸發安全告警。

  4. 錯誤處理getservbyname可能返回false ,請在代碼中顯式處理,而不是直接使用返回值。

小結

  • 使用getservbyname($service, $protocol)可以判斷服務名稱在本機系統服務數據庫中是否有端口映射(返回端口號或false )。

  • 判斷有效性時最好檢查返回值類型並確保端口在1–65535 範圍內。

  • 該方法不代表服務可用;若需確認可用性,還需進一步做網絡連接層面的檢測(如fsockopen等)。

  • 注意平台差異:不同系統的服務數據庫條目不盡相同,如有跨平台需求請準備回退映射或詢問用戶提供端口號。