如何将 HTTP 标头/正文与 PHP 套接字请求隔离开来
Posted
技术标签:
【中文标题】如何将 HTTP 标头/正文与 PHP 套接字请求隔离开来【英文标题】:How to isolate the HTTP headers/body from a PHP Sockets request 【发布时间】:2012-02-04 02:02:31 【问题描述】:我在 php 中使用套接字连接将数据发布到 Apache 网络服务器。我对这种技术有点陌生,我不确定如何将标头与响应正文隔离开来。
发送代码:
<?php
// collect data to post
$postdata = array(
'hello' => 'world'
);
$postdata = http_build_query($postdata);
// open socket, send request
$fp = fsockopen('127.0.0.1', 80);
fwrite($fp, "POST /server.php HTTP/1.1\r\n");
fwrite($fp, "Host: fm1\r\n");
fwrite($fp, "Content-Type: application/x-www-form-urlencoded\r\n");
fwrite($fp, "Content-Length: ".strlen($postdata)."\r\n");
fwrite($fp, "Connection: close\r\n");
fwrite($fp, "\r\n");
fwrite($fp, $postdata);
// go through result
$result = "";
while(!feof($fp))
$result .= fgets($fp);
// close
fclose($fp);
// display result
echo $result;
?>
服务器代码:
Hello this is server. You posted:
<pre>
<?php print_r($_POST); ?>
</pre>
当发布到一台服务器时,我得到:
HTTP/1.1 200 OK
Date: Fri, 06 Jan 2012 09:55:27 GMT
Server: Apache/2.2.15 (Win32) mod_ssl/2.2.15 OpenSSL/0.9.8m PHP/5.3.2
X-Powered-By: PHP/5.3.2
Content-Length: 79
Connection: close
Content-Type: text/html
Hello this is server. You posted:
<pre>
Array
(
[hello] => world
)
</pre>
正如预期的那样。不过,我想去掉标题,然后从“你好,这是服务器.....”开始阅读正文。 我怎样才能可靠地检测到标题的结尾并将正文读入变量?
另外,我测试过的另一台服务器的回复是这样的:
HTTP/1.1 200 OK
Date: Fri, 06 Jan 2012 10:02:04 GMT
Server: Apache/2
X-Powered-By: PHP/5.2.17
Connection: close
Transfer-Encoding: chunked
Content-Type: text/html
4d
Hello this is server. You posted:
<pre>
Array
(
[hello] => world
)
</pre>
0
正文周围的“4d”和“0”是什么??
谢谢!
PS 在有人说使用 CURL 之前,不幸的是我不能 :-(
【问题讨论】:
不确定4d
和0
,但您可以非常可靠地使用explode('\r\n\r\n', $result)
。
【参考方案1】:
标题应以"\r\n\r\n"
结尾(两次)。这些 4d 和 0 可能是您的 php 响应的一部分(它们不是标头的一部分)。
【讨论】:
【参考方案2】:您可以通过双换行符将标题与正文分开。它应该是<CRLF><CRLF>
,所以这通常会起作用:
list($header, $body) = explode("\r\n\r\n", $response, 2);
更可靠的是,您应该使用正则表达式来捕获换行符变化(极不可能发生):
list($header, $body) = preg_split("/\R\R/", $response, 2);
带有4d
和0
的东西称为chunked encoding。 (它是用另一个换行符分隔的十六进制数字,并指示以下原始内容块的长度。
要清除它,您必须先查看标题,然后查看是否有相应的 Transfer-Encoding:
条目。如果它变得复杂,建议使用无数现有的 HTTP 用户空间处理类之一。梨has one.
【讨论】:
太好了,您拆分标题的方法似乎效果很好。此外,看起来您对数字的看法是正确的。它们确实会根据内容的长度而变化。我希望我可以使用 CURL,我会研究 PEAR 来处理它。非常感谢! 您应该能够在请求时使用 HTTP/1.0 以避免解析块。【参考方案3】:在大多数情况下,马里奥的答案应该有效,但我刚刚尝试将此方法应用于 Couch DB 的响应,并且在某些情况下它不起作用。
如果响应不包含任何文档,则 Couch DB 将 "\r\n\r\n" 放在响应正文中,试图保持结果格式正确,在这种情况下,仅将响应拆分为 "\r 是不够的\n\r\n" 因为你可能会不小心切割身体的末端。
HTTP/1.0 200 OK Server: CouchDB/1.6.1 (Erlang OTP/R16B02) ETag: "DJNMQO5WQIBZHFMDU40F1O94T" Date: Mon, 06 Jul 2015 09:37:33 GMT Content-Type: text/plain; charset=utf-8 Cache-Control: must-revalidate
"total_rows":0,"offset":0,"rows":[
// Couch DB adds some extra line breakers on this line
]
以下解析似乎对 Couch DB 更可靠:
$parts = explode("\r\n\r\n", $response);
if ($parts)
$headers = array_shift($parts);
$body = json_decode(implode("\r\n\r\n", $parts));
【讨论】:
FWIW:他的回答仍然对你有用。关键是explode
的第三个参数中的“2”。意味着在使用explode
创建的数组中最多只能有 2 个项目。因此,在list($header, $body)
中,数组的第一个“块”将分配给$headers
,而其他所有内容,无论存在多少\r\n
,都将分配给$body
。以上是关于如何将 HTTP 标头/正文与 PHP 套接字请求隔离开来的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Flutter/Dart 中使用 url 编码的标头和正文发出 HTTP POST 请求
使用 http 端点访问 lambda 中的 HTTP 请求(标头、查询字符串、cookie、正文)对象
从 PHP 中的 JSON POST 读取 HTTP 请求正文的问题