PHP http协议封装原理


http协议封装原理


正文

什么是HTTP

你可能会好奇HTTP,HTTP是什么?

HTTP协议(HyperText Transfer Protocol,超文本传输协议)是用于从WWW服务器传输超文本到本地浏览器的传送协议。 HTTP是一个基于TCP/IP通信协议来传递数据(HTML 文件, 图片文件, 查询结果等)的协议。

HTTP的发展是万维网协会(World Wide Web Consortium)和Internet工作小组IETF(Internet Engineering Task Force)合作的结果, 最终发布了一系列的RFC,RFC 1945定义了HTTP/1.0版本。其中最著名的就是RFC 2616。RFC 2616定义了今天普遍使用的一个版本——HTTP 1.1。

HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP是一个无状态的协议。

HTTP协议通常承载于TCP协议之上,有时也承载于TLS或SSL协议层之上,这个时候,就成了我们常说的HTTPS。

默认HTTP的端口号为80,HTTPS的端口号为443。

HTTP协议永远都是客户端发起请求,服务器回送响应。无法实现在客户端没有发起请求的时候,服务器将消息推送给客户端。

HTTP协议是一个无状态的协议,同一个客户端的这次请求和上次请求没有对应关系。

HTTP 工作原理

HTTP协议工作于客户端-服务端架构上。浏览器作为HTTP客户端通过URL向HTTP服务端即WEB服务器发送所有请求。

Web服务器有:Apache服务器,Nginx服务器,IIS服务器(Internet Information Services)等。

Web服务器根据接收到的请求后,向客户端发送响应信息。

HTTP默认端口号为80,但是你也可以改为8080或者其他端口。

HTTP三点注意事项:

  • HTTP是无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间。
  • HTTP是媒体独立的:这意味着,只要客户端和服务器知道如何处理的数据内容,任何类型的数据都可以通过HTTP发送。客户端以及服务器指定使用适合的MIME-type内容类型。
  • HTTP是无状态:HTTP协议是无状态协议。无状态是指协议对于事务处理没有记忆能力。缺少状态意味着如果后续处理需要前面的信息,则它必须重传,这样可能导致每次连接传送的数据量增大。另一方面,在服务器不需要先前信息时它的应答就较快。

下图展示了HTTP协议通信流程:

HTTP 消息结构

HTTP是基于客户端/服务端(C/S)的架构模型,通过一个可靠的链接来交换信息,是一个无状态的请求/响应协议。

一个HTTP”客户端”是一个应用程序(Web浏览器或其他任何客户端),通过连接到服务器达到向服务器发送一个或多个HTTP的请求的目的。

一个HTTP”服务器”同样也是一个应用程序(通常是一个Web服务,如Apache Web服务器或IIS服务器等),通过接收客户端的请求并向客户端发送HTTP响应数据。

HTTP使用统一资源标识符(Uniform Resource Identifiers, URI)来传输数据和建立连接。

一旦建立连接后,数据消息就通过类似Internet邮件所使用的格式[RFC5322]和多用途Internet邮件扩展(MIME)[RFC2045]来传送。

  • 客户端请求消息

客户端发送一个HTTP请求到服务器的请求消息包括以下格式:请求行(request line)、请求头部(header)、空行和请求数据四个部分组成, 下图给出了请求报文的一般格式:

示例:

GET /hello.txt HTTP/1.1
User-Agent: curl/7.16.3 libcurl/7.16.3 OpenSSL/0.9.7l zlib/1.2.3
Host: www.example.com
Accept-Language: en, mi

上面示例中请求方法是GET。

根据 HTTP 标准,HTTP 请求可以使用多种请求方法。

HTTP1.0 定义了三种请求方法: GET, POST 和 HEAD方法。

HTTP1.1 新增了六种请求方法: OPTIONS、PUT、PATCH、DELETE、TRACE 和 CONNECT 方法,加上GET, POST 和 HEAD方法,共9中。

  • 服务器响应消息

HTTP响应也由四个部分组成,分别是:状态行、消息报头、空行和响应正文:

示例:

HTTP/1.1 200 OK
Date: Mon, 27 Jul 2009 12:28:53 GMT
Server: Apache
Last-Modified: Wed, 22 Jul 2009 19:15:56 GMT
ETag: "34aa387-d-1568eb00"
Accept-Ranges: bytes
Content-Length: 51
Vary: Accept-Encoding
Content-Type: text/plain

Hello World!

http协议封装实现

php中对http协议是怎么封装实现的,属于底层。封装后的库有socket、fsockopen、curl

PHP 带有很多内置 URL 风格的封装协议,可用于类似 fopen()、 copy()、 file_exists() 和 filesize() 的文件系统函数。 除了这些封装协议,还能通过 stream_wrapper_register() 来注册自定义的封装协议。

php协议类型

file:// — 访问本地文件系统
http:// — 访问 HTTP(s) 网址
ftp:// — 访问 FTP(s) URLs
php:// — 访问各个输入/输出流(I/O streams)
zlib:// — 压缩流
data:// — 数据(RFC 2397)
glob:// — 查找匹配的文件路径模式
phar:// — PHP 归档
ssh2:// — Secure Shell 2
rar:// — RAR
ogg:// — 音频流
expect:// — 处理交互式的流

PHP.ini

allow_url_fopen :on 默认开启 该选项为on便是激活了 URL 形式的 fopen 封装协议使得可以访问 URL 对象文件等。
allow_url_include:off 默认关闭,该选项为on便是允许 包含URL 对象文件等

基于java和php的http协议处理方式对比

基于java和PHP对一个http请求进行处理的方式可以用下面这个简单的图示体现出来:

1.处理PHP程序的php-fpm服务接收到的是基于CGI(通用网关接口)的数据协议封装过的http数据,所以只要你精通CGI协议,理论上可以自己实现一个类似php-fpm的服务接收CGI数据,然后用自创的一门脚本语言去处理http数据。

2.tomcat是一个web服务容器,它可以像nginx一样监听一个IP和端口,接收http协议的数据。只不过它自身就拥有处理http协议数据的能力,而nginx只能转发给其他模块处理,nginx要不就通过ngx_http_proxy_module将http协议的数据进行转换,交给php-fpm这类的CGI代理服务器用PHP之类的脚本语言处理,要不就利用反向代理的方式直接将数据原样转发给tomcat去处理。由于tomcat可以直接处理http协议数据,所以它可以直接面向客户端,不过一般不会这样做。

3.php-fpm是基于c实现的服务,采用多进程的方式运行,每个http请求由单个进程处理。tomcat是基于java实现的,服务运行在jvm虚拟机内,服务运行采用多线程的方式,每个http请求由单个线程处理,tomcat对http请求的处理是将http的请求和返回数据进行封装后交给一个实现了servlet接口的类去处理,这里体现了java的面向接口编程的特点。

4.php-fpm在处理http请求时,最终会将PHP脚本交给zend引擎处理,这里在每个http请求处理的过程中都要执行一些PHP代码中的初始化逻辑,这是php性能优化的关键和难点所在。而tomcat服务在启动过程中会执行大量java代码的初始化逻辑(参考spring框架的依赖注入——DI),虽然服务启动时间较长,但是在处理请求的过程中省去了大量初始化的时间,处理时间就短了。

Base64编码

为什么HTTP传输时,需要进行Base64编码呢?

理解清楚Base64,就需要搞清楚Base64与ASCII码的关系。

Base64是一种编码算法,只支持64个可打印字符,范围是0 00000000 - 63 00111111

ASCII码的范围是0 00000000 -127 01111111,其中0-31和127这33个字符属于控制字符(Control characters), 剩下32-126这95个字符属于可打印字符(Printable characters),包含数字、大小写字母、常用符号。

控制字符:

可打印字符:

因为历史原因,早期一些传输协议只能传输ASCII字符,导致8Bit字节码(0-255)超出了可用范围。 传输图片时,一些Byte值,如187 10111011不属于ASCII码范围,因此无法被传输。 既然向上超过127的不能被传输,那么向下压缩呢,如用6Bit字节的Base64来表示8Bit字节码(0-255)呢。

8和6的最小公倍数是24,这意味着可以用4个Base64字符来表示3个8Bit字符,如:

最终8Bit字符串Man编码成了Base64字符串TWFu

相比8bit的字符编码,Base64编码会多占用三分之一的字节长度(前面补0),以此为代价实现了更好的兼容性。

若原本8Bit字符串长度不是3的倍数,则后面补0,凑成3的倍数。 至于第3第4个Base64字符,没有匹配的8bit字符,则使用=字符填充(padding), 如下,把8bit的M编码成Base64的TQ==

解码时,逆向操作,把 6bit X 4 的字符串每8bit分成一组,共3组,每1组转换成一个8位的Byte字节。

状态行

我们可以指定相应输出的状态行,如:

HTTP/1.1 401 Unauthorized

php实现:

header('HTTP/1.1 401 Unauthorized', true, 401);

看一下,header函数:

/**
 * Send a raw HTTP header
 * @link https://php.net/manual/en/function.header.php
 * @param string $header <p>
 * The header string.
 * </p>
 * <p>
 * There are two special-case header calls. The first is a header
 * that starts with the string "HTTP/" (case is not
 * significant), which will be used to figure out the HTTP status
 * code to send. For example, if you have configured Apache to
 * use a PHP script to handle requests for missing files (using
 * the ErrorDocument directive), you may want to
 * make sure that your script generates the proper status code.
 * </p>
 * <p>
 * The second special case is the "Location:" header. Not only does
 * it send this header back to the browser, but it also returns a
 * REDIRECT (302) status code to the browser
 * unless the 201 or
 * a 3xx status code has already been set.
 * </p>
 * @param bool $replace [optional] <p>
 * The optional replace parameter indicates
 * whether the header should replace a previous similar header, or
 * add a second header of the same type. By default it will replace,
 * but if you pass in false as the second argument you can force
 * multiple headers of the same type. For example:
 * </p>
 * @param int $response_code [optional] <p>
 * Forces the HTTP response code to the specified value.
 * </p>
 * @return void
 */
function header ($header, $replace = true, $response_code = null) {}

$response_code 就是指定的状态码

从PHP 5.4开始,可以使用:

http_response_code(404);

如果curl请求中,我要获取返回的状态码呢,可以使用curl_getinfo() 指定获取CURLINFO_HTTP_CODE

$ch = curl_init('http://www.lezhing.com');
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_exec($ch);
echo curl_getinfo($ch, CURLINFO_HTTP_CODE); // 200
curl_close($ch);






参考资料

深入理解HTTP协议、HTTP协议原理分析 https://www.cnblogs.com/chenliyang/p/6558756.html

HTTP 教程 https://www.runoob.com/http/http-tutorial.html

PHP 手册 语言参考 支持的协议和封装协议 https://www.php.net/manual/zh/wrappers.php

PHP 手册 语言参考 支持的协议和封装协议 http:// – https:// — 访问 HTTP(s) 网址 https://www.php.net/manual/zh/wrappers.http.php

HTTP协议详解由浅入深看HTTP https://www.cnblogs.com/guguli/p/4758937.html

PHP解析Http协议&协议复习 https://blog.csdn.net/suilz/article/details/84359417

关于php支持的协议与封装协议总结(推荐) https://www.jb51.net/article/128429.htm

基于java和php的http协议处理方式对比 https://www.cnblogs.com/imyhb/p/11197269.html

漫画:什么是Base64算法? https://mp.weixin.qq.com/s?__biz=MzIxMjE5MTE1Nw==&mid=2653191459&idx=1&sn=6e7d82dabe9c4a26b55f86f502edac03&chksm=8c990ff9bbee86ef7d6eee8a92430ff529b57e2f8720d439b7a4fb83b9ba75cd91395509a239&scene=21#wechat_redirect


返回