正文
什么是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