HTTP 协议的响应包括响应头和响应体(不是必须的),响应头中的 Content-Length 用来标记响应 Body 的长度,这其实是 HTTP 协议的分包机制。假设实际发送的 Body 长度小于 Content-Length,浏览器会一直处于等待接收数据的状态(小圈一直转)。假设实际发送的 Body 长度大于 Content-Length,多于的部分会被丢弃。
Swoole\Coroutine\run(function () {
$server = new Swoole\Coroutine\Server('127.0.0.1', '9501', false, true);
$server->handle(function (Swoole\Coroutine\Server\Connection $conn) {
while (true) {
$data = $conn->recv();
if (empty($data)) {
$conn->close();
break;
}
$response = new Response();
$response->withHeader('Content-Type', 'text/html;charset=utf-8')
->withHeader('Content-Length', '4')
->withBody('abc');
// 报头标记了 4 个字节的 body, 实际发送了 3 个字节,此时浏览器在转圈
$conn->send($response->getPackage());
// 10 秒后发送最后一个字节,所有数据发送完毕
Swoole\Coroutine::sleep(10);
$conn->send('d');
}
});
$server->start();
});
/**
* 用来简单组装 header 和 body
* Class Response
*/
class Response {
private $header = "HTTP/1.1 200 OK\r\n";
private $body = '';
public function withHeader($name, $value) {
$name = strtolower($name);
if ($name == 'content-type') {
$this->header .= "Content-Type: {$value}\r\n";
} elseif ($name == 'content-length') {
$this->header .= "Content-Length: " . $value . "\r\n";
}
return $this;
}
public function withBody($body) {
$this->body .= $body;
return $this;
}
/**
* 获取组装好的 http 报文
* @return string
*/
public function getPackage() {
return $this->header ."\r\n" . $this->body;
}
}