一次完整的HTTP请求过程
- 流程概况
如图所示,一次完整的HTTP请求过程,包含DNS请求和响应及域名解析,也包括TCP连接,三次握手,以及服务器响应HTTP请求,浏览器对html解析呈现,最后服务器关闭TCP连接,进行四次挥手。 - 过程详解
步骤一:DNS解析
如图所示,DNS
解析过程,首先,PC
会搜索浏览器自身的DNS
缓存(缓存时间比较短,1分钟左右,且只能容纳1000条缓存),如果浏览器自身的缓存里没有,则浏览器会搜索系统自身的DNS缓存,也会尝试去hosts文件里去找,如果上述的三个过程都没有找到,就会递归地去域名服务器去查找,具体如上图。DNS
的本地查询为递归查询,而域名服务器查询为迭代查询。DNS
优化有两个方面包括DNS缓存和DNS负载均衡。
步骤二:TCP
连接--三次握手
三次握手(Three-way Handshake),是指建立一个TCP连接时,需要客户端和服务器总共发送三个数据包。三次握手的目的是连接服务器指定端口,建立TCP连接,并同步连接双方的序列号和确认号,减缓TCP窗口大小信息。在socket
编程中,客户端执行connect()时,将触发三次握手。在客户端获取到域名对应的IP地址后,User-Agent(一般指浏览器)会以一个随机端口(1024<端口<65535)向服务器的Web程序(常见的有httpd,nginx)等的80端口发送请求。这个连接请求(原始的http请求经过TCP/IP4层模型的层层封包)达到服务器后(这中间会有各种路由设备,局域网除外),进入到网卡,然后进入到内核的TCP/IP协议栈(用于识别连接请求,解封包,一层一层的剥开),还有可能要经过Netfliter防火墙(属于内核的模块)的过滤,最终达到Web程序,最终建立了TCP/IP连接。
- 第一次握手(
SYN=1, seq=x
)
客户端发送一个TCP
的SYN
标志位置1
的包,指明客户端打算连接的服务器的端口,以及初始序号X
,保存在包头的序列号(Sequence Number
)字段里。发送完毕后,客户端进入SYN_SEND
状态。 - 第二次握手(
SYN=1, ACK=1, seq=y, ACKnum=x+1
)
服务器发回确认包(ACK)应答。即SYN
标志位和ACK
标志位均为1
.服务器端选择自己ISN
序列号,放到Seq
域里,同时将确认序号(Acknowledgement Number
)设置为客户的ISN
加1
,即x+1
。发送完毕后,服务器端进入SYN_RCVD
状态。 - 第三次握手(
ACK=1, ACKnum=y+1
)
客户端再次发送确认包(ACK
),SYN
标志位为0
,ACK
标志位为1
,并且把服务器发来ACK
的序号字段+1
,放在确认字段中发送给对方,并且在数据段写ISN+1
.发送完毕后,客户端进入ESTABLISHED
状态,当服务器端接收到这个包时,也进入ESTABLISHED
状态,TCP
握手结束,开始传输数据。
步骤三:发起HTTP请求(建立TCP三次握手后)
HTTP请求报文由三部分组成,请求行、请求头、空行、请求正文 - 请求行:用于描述客户端的请求方式(GET/POST等),请求的资源名称(URL)以及使用的HTTP协议的版本号
- 请求头:用于描述客户端请求哪台主机及其端口,以及客户端的一些环境信息。
- 空行:
\r\n
就是空行符 - 请求正文:当使用POST等方法时,通常需要客户端向服务器传递数据。这些数据就储存在请求正文中(GET方式是保存在url地址后面,不会放到这里)
GET请求实例:
POST请求实例:
HTTP协议的请求方法有很多,常见的几种:
- GET:完整请求一个资源(常用)
- HEAD:仅请求响应首部
- POST:提交表单(常用)
- PUT:上传文件(浏览器一般不支持)
- DELETE:删除
- OPTIONS:返回请求的资源所支持的方法的方法
- TRACE:追求一个资源请求中间所经过的代理(该方法不能由浏览器发出)
请求资源的标识
- URI Uniform Resource Identifier 统一资源标识符
- URL Uniform Resource Locator 统一资源定位符
- URN Uniform Resource Name 统一资源名称
URL和URN 都属于 URI,为了方便就把URL和URI暂时都通指一个东西
步骤四:服务器响应http请求,浏览器得到html代码
HTTP响应也由三部分组成:状态行,响应头,空格,消息体
状态行包括协议版本、状态码、状态码描述
状态码:状态码用于表示服务器对请求的处理结果 - 1xx:指示信息——表示请求已经接受,继续处理
- 2xx:成功——表示请求已经被成功接收、理解、接受。
- 3xx:重定向——要完成请求必须进行更进一步的操作
- 4xx:客户端错误——请求有语法错误或请求无法实现
- 5xx:服务器端错误——服务器未能实现合法的请求。
响应头:响应头用于描述服务器的基本信息,以及客户端如何处理数据
空格:CRLF(即 \r\n)分割
消息体:服务器返回给客户端的数据
HTTP响应实例
其中Content-Length
表示内容长度,Content-Type
表示内容类型。
步骤五:浏览器解析html代码,并请求html代码中的资源
浏览器拿到html文件后,就开始解析其中的html代码,遇到js/css/image等静态资源时,就向服务器端去请求下载(会使用多线程下载,每个浏览器的线程数不一样),这是时候就用上 keep-alive特性了,建立一次HTTP连接,可以请求多个资源,下载资源的顺序就是按照代码里面的顺序,但是由于每个资源大小不一样,而浏览器又是多线程请求请求资源,所以这里显示的顺序并不一定是代码里面的顺序。
步骤六:浏览器对页面进行渲染呈现给用户
最后,浏览器利用自己内部的工作机制,把请求的静态资源和html代码进行渲染,渲染之后呈现给用户,浏览器是一个边解析边渲染的过程。首先浏览器解析HTML文件构建DOM树,然后解析CSS文件构建渲染树,等到渲染树构建完成后,浏览器开始布局渲染树并将其绘制到屏幕上。这个过程比较复杂,涉及到两个概念: reflow(回流)和repain(重绘)。DOM节点中的各个元素都是以盒模型的形式存在,这些都需要浏览器去计算其位置和大小等,这个过程称为relow;当盒模型的位置,大小以及其他属性,如颜色,字体,等确定下来之后,浏览器便开始绘制内容,这个过程称为repain。页面在首次加载时必然会经历reflow和repain。reflow和repain过程是非常消耗性能的,尤其是在移动设备上,它会破坏用户体验,有时会造成页面卡顿。所以我们应该尽可能少的减少reflow和repain。JS的解析是由浏览器中的JS解析引擎完成的。JS是单线程运行,JS有可能修改DOM结构,意味着JS执行完成前,后续所有资源的下载是没有必要的,所以JS是单线程,会阻塞后续资源下载。
步骤七:服务器关闭TCP连接
般情况下,一旦Web服务器向浏览器发送了请求数据,它就要关闭TCP连接,然后如果浏览器或者服务器在其头信息加入了这行代码:Connection:keep-alive
。TCP连接在发送后将仍然保持打开状态,于是,浏览器可以继续通过相同的连接发送请求。保持连接节省了为每个请求建立新连接所需的时间,还节约了网络带宽。
断开TCP连接需要四次挥手,中断连接端可以是客户端,也可以是服务器端。
四次挥手: - 第一次挥手:客户端发送一个FIN=M,用来关闭客户端到服务器端的数据传送,客户端进入FIN_WAIT_1状态。表示客户端已经将数据全部发送完毕,但是服务器端如果还有数据需要发送,则不必着急关闭连接,可以继续发送数据。
- 第二次挥手:服务器端收到FIN后,先发送ack=M+1,告知客户端,你的请求我收到,但是我还没有准备好,请继续等我消息。此时客户端进入FIN_WAIT_2状态,继续等待服务器端的FIN报文。
- 第三次挥手:当服务器端确定数据已发送完成,则向客户端发送FIN=N报文,告诉客户端,此时服务器端数据发送完成,准备好关闭连接,服务器进入LAST_ACK状态。
- 第四次挥手:客户端收到FIN=N后,此时可以关闭连接了,但是担心服务器端不知道要关闭,所以发送ack=N+1,进入TIME_WAIT状态,如果服务器端没有收到ACK则可以重传。服务器收到ACK后,就知道可以断开连接了,客户端等待了2MSL后依然没有收到回复,则证明服务器端已正常关闭,此时客户端可以关闭连接了。最终完成了四次挥手。
上面是一方主动关闭,另一方被动关闭的情况,实际中还会出现同时发起主动关闭的情况,也是通过四次握手。
自此完成一次完整的HTTP请求和响应。
Appendix:
- Synchronize Sequence Numbers, SYN, 同步序列编号。 该标志仅在三次握手建立TCP连接时有效。它提示TCP连接的服务端检查序列编号,该序列编号为TCP连接初始端(一般是客户端)的初始序列编号。
- Acknowledgement Number, ACK, 确认编号。大多数情况下该标志位是置位的。TCP报头内的确认编号栏内包含的确认编号为下一个预期的序列编号,同时提示远端系统已经成功接收所有数据。
TCP协议工作在传输层,是一种可靠的面向连接的数据流协议。TCP之所以可靠,是因为它保证了传送数据包的顺序。顺序是用一个序列号来保证的。响应包内也包括一个序列号,表示接收方准备好这个序列号的包。在TCP传送一个数据包时,它会把这个数据包放入重发队列中,同时启动计时器,如果收到了关于这个包的确认信息,便将此数据包从队列中删除,如果在计时器超时的时候仍然没有收到确认信息,则需要重新发送该数据包。另外,TCP通过数据分段中的序列号来保证所有传输的数据可以按照正常的顺序进行重组,从而保证数据传输的完整。
- Inital Sequence Number, ISN, 初始序列号。是客户端随机产生的一个值,确认号是0;服务器收到这个同步请求数据包后,会对客户端进行一个同步确认。这个数据包中,序列号(ISN)是服务器随机产生的一个值,确认号是客户端的初始序列号+1;客户端收到这个同步确认数据包后,再对服务器进行一个确认。该数据包中,序列号是上一个同步请求数据包中的确认号值,确认号是服务器的初始序列号+1。
- Maximum Segment Lifetime, MSL, 报文最大生存时间.