首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> 網頁程式設計 >> PHP 程式設計 >> PHP 相關知識

PHP 純 Socket 實作 HTTP 請求(不依賴 cURL/擴展)

     編輯:PHP 相關知識

在無法安裝 cURL 或 pecl_http 擴展的受限環境,可使用 PHP 內建 fsockopen() 手工組裝 HTTP 封包,實作完整的 GET/POST 請求,支援 Header 自訂、Cookie、逾時、阻塞/非阻塞模式。

通用 httpRequest 函式

<?php
/**
 * 純 Socket HTTP 請求
 * @param string $url 完整 URL
 * @param array|string $post POST 資料
 * @param string $method GET|POST
 * @param int $limit 讀取長度限制 (0=無限制)
 * @param bool $returnHeader 是否回傳 Header
 * @param string $cookie Cookie 字串
 * @param bool $bySocket 強制使用 Socket (保留相容)
 * @param string $ip 指定解析 IP
 * @param int $timeout 逾時秒數
 * @param bool $block 阻塞模式
 * @return string|array|false 內容/含Header/失敗false
 */
function httpRequest(
    string $url,
    $post = '',
    string $method = 'GET',
    int $limit = 0,
    bool $returnHeader = false,
    string $cookie = '',
    bool $bySocket = false,
    string $ip = '',
    int $timeout = 15,
    bool $block = true
) {
    $parsed = parse_url($url);
    $host = $parsed['host'] ?? '';
    $path = ($parsed['path'] ?? '/') . (isset($parsed['query']) ? '?' . $parsed['query'] : '');
    $port = $parsed['port'] ?? ($parsed['scheme'] === 'https' ? 443 : 80);

    // HTTPS 需使用 ssl:// 前綴
    $fpHost = ($parsed['scheme'] === 'https' ? 'ssl://' : '') . ($ip ?: $host);

    $out = "$method $path HTTP/1.0\r\n";
    $out .= "Host: $host\r\n";
    $out .= "User-Agent: {$_SERVER['HTTP_USER_AGENT'] ?? 'PHP Socket Client'}\r\n";
    $out .= "Accept: */*\r\n";
    $out .= "Connection: Close\r\n";
    if ($cookie) $out .= "Cookie: $cookie\r\n";

    if (strtoupper($method) === 'POST') {
        $postData = is_array($post) ? http_build_query($post) : $post;
        $out .= "Content-Type: application/x-www-form-urlencoded\r\n";
        $out .= "Content-Length: " . strlen($postData) . "\r\n";
        $out .= "\r\n$postData";
    } else {
        $out .= "\r\n";
    }

    $fp = fsockopen($fpHost, $port, $errno, $errstr, $timeout);
    if (!$fp) {
        error_log("Socket 連線失敗 [$errno]: $errstr");
        return false;
    }

    stream_set_blocking($fp, $block);
    stream_set_timeout($fp, $timeout);
    fwrite($fp, $out);

    $header = $content = '';
    $meta = stream_get_meta_data($fp);

    if (!$meta['timed_out']) {
        // 讀取 Header
        while (!feof($fp)) {
            $line = fgets($fp);
            $header .= $line;
            if ($line === "\r\n" || $line === "\n") break;
        }
        // 讀取 Body
        while (!feof($fp) && (!$limit || $limit > 0)) {
            $chunk = fread($fp, $limit ? min(8192, $limit) : 8192);
            $content .= $chunk;
            if ($limit) $limit -= strlen($chunk);
        }
    } else {
        error_log("Socket 讀取逾時");
    }
    fclose($fp);

    return $returnHeader ? ['header' => $header, 'content' => $content] : $content;
}

// 使用範例
echo httpRequest('https://www.baidu.com');
?>

關鍵實作細節

  • HTTPS 支援:fsockopen('ssl://host', 443) 需 OpenSSL 擴展(PHP 核心內建)。
  • Header 結束判斷:\r\n\r\n 或 \n\n。
  • Chunked Transfer Encoding:此簡易實作不支援,回應若為 chunked 需額外解析。
  • 重導向:需自行解析 Location Header 遞迴請求。
  • 效能:單次請求開銷大於 cURL,高併發場景請改用 cURL 或非同步庫(Swoole、ReactPHP)。

適用場景

  • 嵌入式/精簡環境(OpenWrt、Alpine 精簡版)。
  • 教學理解 HTTP 協定細節。
  • 特殊網路環境需控制 TCP 參數(TTL、TOS、綁定來源 IP)。

雖然功能較 cURL 簡略,但在「零依賴」約束下是可靠的備選方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved