fsockopen 仅建立 TCP 连接,需手动构造 HTTP 请求头、处理响应分隔、解析 Content-Length 或 chunked 编码,并显式关闭连接;它不自动处理重定向、gzip、SSL 证书等,适用极少数底层调试场景。

怎么用 fsockopen 发起 HTTP 请求
fsockopen 不是发 HTTP 请求的“标准工具”,它只负责建立 TCP 连接,后续所有请求头、读取响应、处理分块编码等都得手动写。你得自己拼 GET /path HTTP/1.1,自己加 Host:、Connection:,自己识别 \r\n\r\n 分隔符,自己处理 Content-Length 或 Transfer-Encoding: chunked —— 否则很可能卡住或读不全。
典型流程是:fsockopen 建连 → fwrite 发送完整请求字符串 → fgets/fread 逐行/按长度读响应头 → 找到空行后按 Content-Length 或解析 chunked 数据 → fclose 关闭。
- 必须手动设置
Host头,HTTP/1.1 协议强制要求 - 建议加
Connection: close,避免服务端保持连接导致fread阻塞 - 超时要用
fsockopen($host, $port, $errno, $errstr, $timeout)的第 5 个参数,不能依赖后续读取的超时 - HTTPS 要用
ssl://或tls://前缀,且需确保 OpenSSL 扩展启用
fsockopen 和 curl 的关键区别在哪
fsockopen 是裸 socket,curl 是封装好的 HTTP 客户端。前者给你自由(比如自定义协议、中间人调试),后者给你省事(自动重定向、Cookie 管理、gzip 解压、证书校验)。
常见误判是认为 fsockopen “更底层所以更快”——实际在普通 HTTP 场景下,它的性能优势几乎为零,反而因缺少缓冲和复用,频繁建连时更慢。真正需要它的场景极少:比如要测三次握手耗时、模拟特定非法请求头、对接非标 TCP 服务(如 SMTP、Redis 原生命令)。
立即学习“PHP免费学习笔记(深入)”;
-
curl默认走keep-alive,fsockopen每次都是新连接 -
curl自动处理 301/302 重定向;fsockopen收到 3xx 状态码后,你得自己解析Location并重发请求 -
curl可设CURLOPT_SSL_VERIFYPEER控制证书校验;fsockopen连ssl://时若证书无效,默认直接失败,无开关
常见卡死和读不到响应的原因
最常发生的不是连不上,而是连上了却一直 fread 不返回——本质是没按 HTTP 协议规范收尾。
- 忘了在请求末尾加两个
\r\n(即空行),服务端一直在等请求体,不发响应 - 读响应头时用了
fgets但没检查是否返回false(连接关闭),导致死循环 - 响应含
Transfer-Encoding: chunked,但代码只按Content-Length读,漏掉后续 chunk - 服务端返回了压缩内容(
Content-Encoding: gzip),而你没解压,把二进制当字符串输出乱码 -
端口写错:HTTP 是
80,HTTPS 是443,但用ssl://时仍要传443,不能传80
一个最小可用的 GET 示例(带错误防护)
以下仅作协议验证用,别直接扔进生产:
$fp = fsockopen('tcp://example.com', 80, $errno, $errstr, 5);
if (!$fp) {
die("Connect failed: $errstr ($errno)");
}
fwrite($fp, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n");
$headers = '';
while (($line = fgets($fp)) !== false && $line !== "\r\n") {
$headers .= $line;
}
// 解析 Content-Length
preg_match('/Content-Length:\s*(\d+)/i', $headers, $m);
$len = $m[1] ?? 0;
$body = $len ? fread($fp, $len) : '';
fclose($fp);
注意:这个例子不处理 chunked、不处理重定向、不处理 gzip、不验证状态码。真要可靠通信,curl 或 file_get_contents with stream_context_create 是更实际的选择。
真正难的从来不是连上,而是连上之后,怎么判断响应已经完整结束——这恰恰是 HTTP 协议细节最密集的地方。











