[HTTP] PHP 实现 HTTP Server 原理
单进程服务器简陋版:
<?php
/**
* Single http server.
*
* Access http://127.0.0.1:8081
*
* @license Apache-2.0
* @author farwish
*/ $s_socket_uri = 'tcp://0.0.0.0:8081';
$s_socket = stream_socket_server($s_socket_uri, $errno, $errstr) OR
trigger_error("Failed to create socket: $s_socket_uri, Err($errno) $errstr", E_USER_ERROR); while(1)
{
while($connection = @stream_socket_accept($s_socket, 60, $peer))
{
echo "Connected with $peer. Request info...\n"; $client_request = "";
// Read until double \r
while( !preg_match('/\r?\n\r?\n/', $client_request) )
{
$client_request .= fread($connection, 1024);
} if (!$client_request)
{
trigger_error("Client request is empty!");
}
$headers = "HTTP/1.1 200 OK\r\n"
."Server: nginx\r\n"
."Content-Type: text/html; charset=utf-8\r\n"
."\r\n";
$body = "<h1>hello world</h1><br><br>";
if ((int) fwrite($connection, $headers . $body) < 1) {
trigger_error("Write to socket failed!");
}
fclose($connection);
}
}
HTTP 底层基于 TCP,所以 socket 地址指定为 tcp 协议没有问题;stream_socket_server 功能相当于执行了 socket => bind => listen,stream_socket_accept 阻塞等待 client 连接,并设置了超时时间,默认的 timeout 时间使用在 php.ini 中设置。
注意这里的错误抑制符@,抑制 accept 超时情况产生的 PHP Warning,如果用到 stream_select 也需要加错误抑制符 @ 来避免中断信号引起的 PHP Warning。
HTTP 响应报文格式包含 响应状态码、响应首部、响应主体(如果有的话),响应首部每行以 \r\n 结尾,响应头部结束单独一行 \r\n 结尾,后面就是响应主体了,响应头部加响应主体以 fwrite 写入 socket 连接,fclose 关闭连接。
注意:上面的简陋版既没有设置 socket 上下文选项,也没有使用 I/O 复用,更不是多进程的,只是作为请求响应的演示。
较为严谨的HTTP协议处理版:
$method = '';
$url = '';
$protocol_version = ''; $request_header = [];
$content_type = 'text/html; charset=utf-8';
$content_length = 0;
$request_body = '';
$end_of_header = false; // @see http://php.net/manual/en/function.fread.php
$buffer = fread($connection, 8192); if (false !== $buffer) { // Http request format check.
if (false !== strstr($buffer, "\r\n")) {
$list = explode("\r\n", $buffer);
} if ($list) {
foreach ($list as $line) {
if ($end_of_header) {
if (strlen($line) === $content_length) {
$request_body = $line;
} else {
throw new \Exception("Content-Length {$content_length} not match request body length " . strlen($line) . "\n");
}
break;
} if ( empty($line) ) {
$end_of_header = true;
} else {
// Header.
//
if (false === strstr($line, ': ')) {
$array = explode(' ', $line); // Request line.
if (count($array) === 3) {
$method = $array[0];
$url = $array[1];
$protocol_version = $array[2];
}
} else {
$array = explode(': ', $line); // Request header.
list ($key, $value) = $array;
$request_header[$key] = $value; if ( strtolower($key) === strtolower('Content-type') ) {
$content_type = $value;
} // Have request body.
if ($key === 'Content-Length') {
$content_length = $value;
}
}
}
}
}
} // No request body, show buffer from read.
$response_body = $request_body ?: $buffer;
$response_header = "HTTP/1.1 200 OK\r\n";
$response_header .= "Content-type: {$content_type}\r\n";
if (empty($content_length) && (strlen($response_body) > 0)) {
$response_header .= "Content-Length: " . strlen($response_body) . "\r\n";
}
foreach ($request_header as $k => $v) {
$response_header .= "{$k}: {$v}\r\n";
}
$response_header .= "\r\n";
fwrite($connection, $response_header . $response_body);
fclose($connection);
以上程序属于 accept 之后的处理步骤,外层逻辑这里已省略,也适用在多进程服务器中子进程的处理部分。
这里还是用 fread 统一读取数据,设置读取的长度 8192(fread 的默认也是8192),$buffer 含有头部信息和数据,按 \r\n 分解成数组元素再处理,处理方式按照 HTTP 请求报文格式。
首先是请求行,如 GET /index HTTP/1.1 三部分以 “空格” 隔开,行尾以 \r\n 结束。
其次是报文首部,如 Content-Type: text/html 键值中间以 “冒号” 加 “空格” 隔开,行尾以 \r\n 结束。
请求报文头部以一行 \r\n 结束。
最后是请求主体(如果有的话),如果报文首部中有 Content-Length 值,就说明有请求主体。
响应数据按照 响应头部 加 响应主体,写入 socket 连接,最后关闭连接。
HTTP协议格式示意:
小结:
完整步骤 1.创建服务端套接字 2.接受请求 3.解释请求行确定请求的特定文件 4.从文件系统中获取请求文件 5.创建被请求的文件组成的HTTP响应报文,发送给客户端 6.如果请求的文件不存在,服务端返回 404 Not Found 报文。
无重逻辑(字符输出)的场景中,ab 100并发1000次访问压测,对比传统调优后的 Nginx + PHP,PHP 实现的多进程非阻塞 I/O HTTP Server 的 QPS 性能稍高,有一个理由可以解释:它不需要 Nginx 来转发请求。
PHP实现的 HTTP Server 有很多细节需要自身处理,不同因素会对处理请求的性能产生直接影响以及面临某些场景下才能产生的BUG,所以稳定性上需要经受更多考验。
Reference: https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol#Overview
Link:http://www.cnblogs.com/farwish/p/8418969.html
[HTTP] PHP 实现 HTTP Server 原理的更多相关文章
- MXNet之ps-lite及parameter server原理
MXNet之ps-lite及parameter server原理 ps-lite框架是DMLC组自行实现的parameter server通信框架,是DMLC其他项目的核心,例如其深度学习框架MXNE ...
- Spark job server原理初探
Spark job server是一个基于Spark的服务系统,提供了管理SparkJob,context,jar的RestFul接口. 专注标注原文链接 http://www.cnblogs.com ...
- Tomcat Server 原理
构成: 1.server代表整个catalina serverlet容器 2.service:由一个或多个connector以及一个共享的engine处理引擎组成 3.connector 在指定端口上 ...
- kubernetes核心原理之API Server原理分析
kubernetes API Server的核心功能是提供了Kubernetes各类资源对象(Pod,RC,Service等)的增删改查及Watch等HTTP Rest接口,成为集群内各个功能模块之间 ...
- [原创]安全系列之端口敲门服务(Port Knocking for Ubuntu 14.04 Server)
Port Knocking for Ubuntu 14.04 Server OS:ubuntu 14.04 server 原理简单分析: 端口敲门服务,即:knockd服务.该服务通过动态的添加ipt ...
- FQ原理
笔者在nginx反向代理篇讲了正向代理和反向代理的区别,今天着重讲其中的FQ是实现原理. 一.普遍的两种方式 1.vpn vpn它将客户端的IP数据报经过加密和二次封装后转发出去,客户端通过vpn上网 ...
- kubernetes 核心原理
3.1 K8s API Server 原理分析 K8s API server核心提供对各种资源对象的增.删.改.查以及Watch等HTTPRest接口,是集群内各个模块之间数据交互和通信的中心枢纽,是 ...
- 028.核心组件-API Server
一 Kubernetes API Server原理 1.1 API Server功能 Kubernetes API Server的核心功能是提供Kubernetes各类资源对象(如Pod.RC.Ser ...
- 2021升级版微服务教程6—Ribbon使用+原理+整合Nacos权重+实战优化 一篇搞定
2021升级版SpringCloud教程从入门到实战精通「H版&alibaba&链路追踪&日志&事务&锁」 教程全目录「含视频」:https://gitee.c ...
随机推荐
- 模板层(template)
模板: 什么是模板? html+模板语法 模板语法: 1 变量:{{}} 深度查询: 通过句点符号 . 过滤器 filter {{var|filter_name}} 2 标签: {% tag %} f ...
- HTML5无刷新修改Url,history pushState/replaceState
一.认识window.history window.history表示window对象的历史记录,是由用户主动产生,并且接受javascript脚本控制的全局对象.window对象通过history对 ...
- NOI2017&&codeM2017游记
7.17 坐了一天动车到绍兴,宿舍环境什么的还是很棒的. 7.18 早上开幕式,没啥好看的,例行节目+讲话. 下午笔试,顺利满分,不过ccz挂了一道多选,99分,影响应该不是很大. 练习赛出人意料地没 ...
- COGS 1299. bplusa【听说比a+b还要水的大水题???】
1299. bplusa ☆ 输入文件:bplusa.in 输出文件:bplusa.out 评测插件 时间限制:1 s 内存限制:128 MB [题目描述] 输入一个整数n,将其拆为两 ...
- git学习四:eclipse使用git提交项目
支持原创:http://blog.csdn.net/u014079773/article/details/51595127 准备工作: 目的:eclipse使用git提交本地项目,提交至远程githu ...
- SSM框架原理,作用及使用方法
---恢复内容开始--- 尊重原创:http://m.blog.csdn.net/dennis_wu_/article/details/73437097 作用: SSM框架是spring MVC ,s ...
- 文件末尾判断feof
feof 作用:如果文件结束,则返回非0值,否则返回0 但要注意的是feof要读取到文件结束标志EOF后,才能判断文件是否结束. 所以使用while(!feof(pFile))会出现最后fread会返 ...
- 试用最强Spark IDE--IDEA
1.安装IntelliJ IDEA IDEA 全称 IntelliJ IDEA,是java语言开发的集成环境,IntelliJ在业界被公认为最好的java开发工具之一,尤其在智能代码助手.代码自动提示 ...
- Vue2反向代理
前一段时间写了一个vue2的小项目,用的是vue-cli脚手架搭建的项目,项目里需要跨域,但又不能使用jsonp,上网查了一下,发现有一个之前没接触过的词语--反向代理. 什么是"反向代 ...
- SQL的各种连接(cross join、inner join、full join)的用法理解
SQL中的连接可以分为内连接,外连接,以及交叉连接 . 1. 交叉连接CROSS JOIN 如果不带WHERE条件子句,它将会返回被连接的两个表的笛卡尔积,返回结果的行数等于两个表行数的乘积: 举例, ...