HTTP协议详解
1. HTTP协议概述
HTTP协议是整个web的基础,是客户端和服务器端协同工作的基石,要想了解web的工作原理、优化web应用,就要完全理解HTTP协议。作为专注于web编程的PHP语言,简单的网络模型和接口,使得在PHP中实现套接字、curl等变得极其简单。
简单来说,HTTP就是一个基于应用层的通信规范,它不仅保证计算机正确快速地传输超文本文档,还能确定传输文档中的哪一部分,以及哪部分的内容首先显示(如文本先与图片)等。
超文本传输协议(HTTP,HyperText Transfer Protocol)是互联网上应用最为广泛的一种网络协议,目前普遍使用的版本是 HTTP 1.1。
HTTP是一个应用层协议,由请求和响应构成,是一个标准的客户端服务器模型。HTTP通常承载于TCP协议之上,有时也承载于TLS或SSL协议之上,这时就成了HTTPS。默认HTTP的端口为80,HTTPS的端口为443。
HTTP协议的模型就是客户端发起请求,服务器进行响应,它是一个无状态的协议,同一个客户端的这次请求和上次请求没有对应关系。这种设计属于典型的“问答式”交互,客户端和服务器端一问一答,使得HTTP协议模型异常简单。但这种设计也存在问题,比如服务器端不会主动向客户端推送(push)数据,一问一答的轮询也会使TCP连接频繁建立和断开,导致其交互效率不高。
浏览网页是HTTP协议的主要应用,但这并不代表HTTP协议就只能应用于网页的浏览。只要通信的双方都遵守HTTP协议,它就有用武之地,如QQ、迅雷等。
2. HTTP协议的工作原理
客户端发送一个HTTP请求(request)给服务器,服务器接收到请求后,生成一个响应(response)返回给客户端。一次HTTP操作称为一个事务,其工作过程分为四步:
(1)客户端与服务器建立连接。在浏览器中访问某个网址,HTTP协议开始工作。
(2)建立连接后,客户端发送一个请求给服务器。格式为:前面是统一资源定位符(URL),中间是协议版本号,后面是MIME信息(包括请求修饰符、客户机信息和可能的内容)。
(3)服务器收到请求后,返回对应的响应信息。格式为:首先是一个状态行(包括信息的协议版本号、状态码),然后是MIME信息(包括服务器信息、实体信息和可能的内容)。
(4)客户端接收服务器返回的信息并呈现给用户,然后断开与服务器的连接。
如果以上过程中的某一步出错,产生的错误信息将返回给客户端。
3. HTTP请求
浏览器客户端在发起请求前,需要先和服务器建立连接。
连接是一个传输层的实际环流,它建立在两个相互通信的应用程序之间。在 HTTP 1.1 协议中,request和response头中都有可能出现一个connection的头,它决定当客户端和服务器通信时如何处理长连接。
HTTP 1.1 协议中,客户端和服务器端默认对方支持长连接,如果客户端不希望使用长连接,需要在header中指明connection的值为close;如果服务器端不想支持长连接,则需要在response中明确说明connection的值为close。无论request还是response的header中包含了值为close的connection,都表明当前正在使用的TCP连接为短连接,即一次请求处理完毕后会断开连接,再次请求时,需重新建立连接。
HTTP请求由三部分组成:请求行、消息报头、请求正文。请求行以一个请求类型开头,以空格分开,后面跟着请求的URI和协议的版本,格式如下:
Method Request-URI HTTP-Version CRLF
上述格式中的参数说明如下:
Method:请求的方法类型
Request URI:统一资源标识符
HTTP Version:请求的HTTP协议的版本
CRLF:回车和换行(除了作为结尾的CRLP外,不允许出现单独的CR或LF字符)
下面列出HTTP抓包工具抓取的HTTP请求的原生代码的示例:
GEThttps://demo.com/?id=1HTTP/1.1 Host:demo.com User-Agent:Mozilla/5.0(WindowsNT6.1;rv:48.0)Gecko/20100101Firefox/48.0 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8 Accept-Language:zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding:gzip,deflate Connection:keep-alive Upgrade-Insecure-Requests:1 Cache-Control:max-age=0
请求的方法类型,主要有以下几种:
GET:请求获取Request URI所标识的资源
POST:在Request URI所标识的资源后附加新的数据
HEAD:请求获取由Request URI所标识的资源的响应消息报头
PUT:请求服务器存储一个资源,并用Request URI作为其标识
DELETE:请求服务器删除Request URI所标识的资源
TRACE:请求服务器回送收到的请求信息,主要用于测试或诊断
CONNECT:保留以备将来使用
OPTIONS:请求查询服务器的性能,或者查询与资源相关的选项和需求
4. HTTP响应
服务器在接收和解释请求消息后,会返回一个HTTP响应(response)消息。HTTP响应也由三个部分组成,分别是:状态行、消息报头、响应正文。状态行格式如下:
HTTP-Version Status-Code Reason-Phrase CRLF
上述格式中的参数说明如下:
HTTP Version:服务器HTTP协议的版本
Status Code:服务器返回的响应状态码
Reason Phrase:状态码的简短描述
下面列出HTTP抓包工具抓取的HTTP响应的原生代码的示例:
HTTP/1.1200OK Date:Sun,04Sep201605:10:43GMT Server:Apache/2.4.10(Win32)OpenSSL/1.0.1iPHP/5.5.15 X-Powered-By:PHP/5.5.15 Content-Length:26 Keep-Alive:timeout=5,max=100 Connection:Keep-Alive Content-Type:text/html Array ( [id]=>1 )
状态码由三位数字组成,第一个数字定义了响应的类别,它有五种可能的值:
1xx:指示信息,请求已接收,继续处理
2xx:成功,请求已被成功接收、理解
3xx:重定向,要完成请求必须进行更进一步的操作
4xx:客户端错误,请求有语法错误或请求无法实现
5xx:服务器端错误,服务器未能实现合法的请求
常见的状态码、状态描述,主要有以下几种:
200 OK:客户端请求成功
400 Bad Request:客户端请求有语法错误,不能被服务器所理解
401 Unauthorize:请求未经授权,这个状态码必须和 WWW Authenticate报头域一起使用
403 Forbidden:服务器收到请求,但是拒绝提供服务
404 Not Found:请求资源不存在,例如输入了错误的URL
500 Internal Server Error:服务器发生了不可预期的错误
503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常
例如,响应状态行“HTTP/1.1 200 OK (CRLF)”,表示请求到达服务器后被成功识别,返回成功标记。响应正文就是服务器返回的资源的内容。
4. 消息报头
HTTP消息报头(header)包括普通报头、请求报头、响应报头、实体报头。每个报头域的组成形式为,名称: 值。
- 普通报头中有少数报头域用于所有的请求和响应消息,但并不用于被传输的实体,只用于传输的消息(如缓存控制、连接控制等)。
- 请求报头允许客户端向服务器端传递请求的附件信息以及客户端自身的消息(如UA头、Accept等)。
- 响应报头允许服务器传递不能放在状态行中的附加响应信息,以及关于服务器的信息和对Request URI所标识的资源进行下一步访问的信息(如Location)。
- 实体报头定义了关于实体正文和请求所标识的资源的元信息,例如有无实体正文。
注意:报头域的名称不区分大小写,报头都是自解释的。
常见的几个比较重要的报头域:
Host:指定了请求资源的主机和端口,必须表示请求URL的原始服务器和网关的位置。HTTP 1.1 请求必须包含主机头域,否则服务器会以400状态码返回。
User-Agent:简称UA,用户代理,指定了发出请求的用户代理的信息。如果是浏览器客户端,则主要包含浏览器的名称、版本和所用的操作系统的内核。UA头不仅仅是使用浏览器才存在,只要使用了基于HTTP协议的客户端都会发送这个报头域。UA头是辨别客户端所用设备(电脑、手机还是iPad等)的重要依据。
Accept:告诉浏览器,客户端可以接收的文件格式。通常这个值在各种浏览器中都差不多,不过WAP浏览器所能接收的格式要少一些,这也是用来区分WAP和计算机浏览器的主要依据之一。但随着WAP浏览器的升级,它已经和计算机浏览器越来越接近,因此这个判断所起的作用也越来越弱。
Cookie:Cookie分两种,一种是客户端向服务器发送的,使用Cookie报头域,用来标记一些信息;另一种是服务器发送给浏览器的,报头为Set Cookie。二者的主要区别是Cookie报头的value里可以有多个Cookie值,并且不需要显示指定domain等。而Set Cookie报头里一条记录只能有一个Cookie的值,需要指明domain、path等。
Cache-Control:缓存控制,指定请求或响应时应该遵循的缓存机制。在请求头或响应头中设置Cache Control并不会修改另一个消息处理过程中的缓存处理过程。请求时的缓存指令包括no cache、no store、max age、max stale、min fresh、only if cached;响应消息中的缓存指令包括public、private、no cache、no store、no transform、must revalidate、proxy revalidate、max age。
Referer:允许客户端指定请求URI的源资源地址,这可以允许服务器生成回退链表,可用来登录、优化缓存等,也允许废除的或错误的连接由于维护的目的被追踪。如果请求的URI没有自己的URI地址,Referer不能被发送。如果指定的是部分URI地址,则此地址应该是一个相对地址。Referer通常是流量统计系统用来记录来访者地址的参数。通过Referer的值,可以得知当前请求是从哪个URL跳转过来的,PHP中可以用$_SERVER['HTTP_REFERER']获取当前请求头中的Referer的值,借此实现网站的防盗链。
Content-Length:内容的长度
Content-Type:内容的类型
Content-Range:资源内容的范围。可以在每次请求中标记请求的资源范围,在连接断开后重连时,客户端只请求该资源未下载的部分,而不是重新请求整个资源,实现断点续传。迅雷就是基于整个原理,使用多线程分段读取网络上的资源,最后再合并。
Accept-Encoding:指定所能接收的编码方式。通常服务器会对返回的内容进行gzip压缩后再输出以减少流量,浏览器一般都支持对这种压缩后的数据进行处理。但对于我们来说,如果不想接收到这些看似“乱码”的数据,可以指定不接收任何服务器的压缩处理,要求其原样返回。
自定义报头:在消息头(header)中,也可以使用一些在 HTTP 1.1 正式规范里没有定义的头字段,这些头字段统称为自定义的HTTP头或扩展头。比如:Server字段,在PHP中,可通过 header 函数实现。