本文面向 C/C++ 開發者,亦適合對 Web 開發歷史感興趣的程序員閱讀。
提到 Web 開發,腦海中浮現的通常是 PHP、Java EE、.NET、Ruby on Rails、Python Django 等現代框架。鮮少有人將 C++ 與 Web 開發聯繫起來。實則在動態腳本語言誕生之前,動態網頁早已存在——其核心技術正是 CGI (Common Gateway Interface,通用網關接口)。
CGI 本質上是一套接口協議,規定了 Web 服務器(如 Apache、Nginx)與外部程序如何交互。任何語言均可編寫 CGI 程序:C/C++、Java、Python、Shell 等。早期 Perl 腳本盛行,故常被稱為「CGI 腳本」,但 C++ 編譯生成的可執行文件同樣屬於 CGI 程序。
雖然在 PHP/Java 統治的今天,C++ 寫 CGI 顯得小眾,但理解其原理有助於掌握 Web 服務的底層運作機制。
瀏覽器發起 HTTP 請求 → Web 服務器接收 → 若請求 CGI 程序,服務器啟動該程序進程 → CGI 程序解析參數、執行邏輯 → 通過標準輸出返回 HTTP 響應(含 Header 與 Body) → 服務器轉發給瀏覽器渲染。
關鍵點: Apache 預設未啟用 CGI,需手動配置(如啟用 mod_cgi、設置 ScriptAlias、賦予執行權限等)。
最基礎的前後端交互方式是 HTML <form> 提交:
<form action="/cgi-bin/hello.cgi" method="get">
<input name="username" />
<input name="password" type="password" />
<input type="submit" value="OK" />
</form>
action:CGI 程序的 URL 路徑method:get(參數拼在 URL 後,可見但不安全)或 post(參數在請求體中,相對安全)Web 服務器將請求信息注入進程環境變量,CGI 程序通過 getenv() 讀取:
| 環境變量 | 含義 |
|---|---|
REQUEST_METHOD | 請求方法:GET/POST |
QUERY_STRING | GET 請求的參數字符串 |
CONTENT_LENGTH | POST 數據長度(從 STDIN 讀取) |
SCRIPT_NAME | CGI 程序路徑 |
SERVER_NAME / SERVER_PORT | 服務器主機/端口 |
這些變量名稱和含義由 CGI 協議標準化,與底層 OS、Web 服務器無關。
通過標準輸出(printf/cout)輸出,必須先輸出 HTTP Header,再輸出兩個換行符,最後輸出 Body:
cout << "Content-Type: text/html\n\n";
cout << "Hello, " << username << "!";
注意: Header 與 Body 之間的空行(兩個 \n)是 HTTP 協議強制要求的。
手工解析參數、管理 Header 繁瑣且易錯。GNU 開源庫 Cgicc 提供:
不足: 無內建 Session 支持。可用 Cookie + 服務端存儲(文件/Redis/Memcached)實現。
傳統 CGI 缺陷: 每個請求都 fork() 一個新進程,高併發下進程創建/銷毀開銷極大。
FastCGI 方案: 常駐內存的進程池。調度器分發請求給工作進程,請求結束後進程不退出,繼續等待下一個請求。PHP-FPM 即 PHP 的 FastCGI 實現;C++ 可用 mod_fcgid 等模塊接入。
隨著前端技術發展(AJAX、SPA),現代 CGI 角色轉變:
這正是當前「前後端分離」架構的雛形。
從 CGI 到 FastCGI,C++ 在 Web 後台經歷了從「請求即進程」到「進程池常駐」的演進。雖然主流選型已轉向高級語言,但 CGI 協議揭示了 Web 服務最本質的交互模型,理解它能幫助我們更透徹地掌握現代 Web 架構的底層邏輯。