我自己构建了一个简单的 HTTP 请求对象,非常基本
class Request {
protected $hostname;
protected $port = 80;
protected $method = 'GET';
protected $uri = '/';
protected $headers = array();
protected $body;
protected $handle;
protected $response;
public function __construct() {
$cookies = null;
foreach (\Cookie::getAll() as $k => $v) {
$cookies .= urlencode($k) . '=' . urlencode($v) . '; ';
}
$this->setHeader('Cookie', substr($cookies, 0, -2));
}
public function setHostname($name, $ssl = false) {
if (filter_var(gethostbyname($name), FILTER_VALIDATE_IP)) {
$this->hostname = ($ssl ? 'ssl://' : null) . $name;
$this->setHeader('Host', $name);
return true;
}
return false;
}
public function setPort($port) {
if ($port >= 1 && $port <= 65535) {
$this->port = (int)$port;
return true;
}
return false;
}
public function setMethod($method) {
$methods = [
'GET' => true,
'POST' => true,
'PUT' => true,
'DELETE' => true
];
if (isset($methods[$method])) {
$this->method = strtoupper($method);
return true;
}
return false;
}
public function setUri($uri) {
$this->uri = $uri;
return true;
}
public function setHeader($key, $value) {
$this->headers[$key] = $value;
return true;
}
public function removeHeader($key) {
unset($this->headers[$key]);
return true;
}
public function setBody($body) {
$this->body = $body;
return true;
}
public function send($async = false, $timeout = 30) {
$errno = null;
$errstr = null;
$this->setHeader('Content-Length', strlen($this->body));
if($async){
$this->setHeader('Connection', 'Close');
}
$this->response = null;
$this->handle = fsockopen($this->hostname, $this->port, $errno, $errstr, $timeout);
if (!$this->handle) {
throw new \Exception($errstr, $errno);
}
$headers = null;
foreach ($this->headers as $key => $value) {
$headers .= $key . ': ' . $value . "\r\n";
}
$request = "{$this->method} {$this->uri} HTTP/1.1\r\n{$headers}\r\n" . $this->body;
fwrite($this->handle, $request);
if ($async) {
fclose($this->handle);
return;
}
while (!feof($this->handle)) {
echo $this->response . ' dgd<br/><br/><br/>';
$this->response .= fgets($this->handle, 8192);
}
fclose($this->handle);
}
public function getResponse() {
return $this->response;
}
}
当我发送“异步”时没有问题,但是当我尝试获取响应时,我收到超出最大执行时间的错误。我在响应读取循环中添加了一个回显来进行调试,它只运行一次。可能是什么问题?
$r = new \Request();
$r->setHostname('localhost');
$r->setUri('/test.php');
$r->send();
echo "<pre>" . print_r($r, true) . "</pre>";
测试.php
echo 'Test complete.';
初始请求的响应来源
dgd<br/><br/><br/><br />
<b>Fatal error</b>: Maximum execution time of 30 seconds exceeded in <b>....\Request.php</b> on line <b>98</b><br />
第 98 行在哪里
$this->response .= fgets($this->handle, 8192);
最佳答案
fgets()
将从套接字读取数据,并在多种条件下完成读取,例如文件结尾、读取的长度 - 1 个字符数或套接字关闭。
您可以尝试的第一件事是将长度更改为更小的值,看看是否可以获得多次读取。这将告诉您循环是否正常工作以及 fgets()
没有看到来自通过套接字发送数据的客户端的正确读取终止行为,该数据正在被 fgets()
读取.
我怀疑正在发送数据的客户端在发送数据后没有关闭套接字。
HTTP 协议(protocol)实际上被设计为无状态的。基本过程是客户端应打开套接字,发送 HTTP 请求,从服务器获取 HTTP 响应,然后关闭套接字。但是,您可以通过在客户端和服务器中进行适当的更改来修改此过程,以便保持发送和接收数据(例如 JSON 或 XML)的连接。
由于 TCP 连接是简单的字节流,因此维护连接的客户端和服务器之间的任何协调都必须在客户端和服务器就数据结构达成某种协议(protocol)的情况下实现。使用 JSON 或 XML 非常简单,因为它们具有消息的结构,您可以在其中知道消息的开头和结尾。
对于使用标准 HTTP 过程的简单 HTTP,消息的结束是通过客户端在收到服务器的 HTTP 响应后关闭套接字来传达的。这告诉服务器通信已完成。
关于PHP fgets 无限期运行,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28322331/