当我们在浏览器网址中输入一个地址,点击回车后发生了以下事情。分为两种情况,http事务和https事务。先讲http事务:
1、浏览器(客户端)进行地址解析。
2、将解析出的域名进行dns解析。
3、通过ip寻址和arp,找到目标(服务器)地址。
4、进行tcp三次握手,建立tcp连接。
5、浏览器发送数据,等待服务器响应。
6、服务器处理请求,并对请求做出响应。
7、浏览器收到服务器响应,得到html代码。
8、渲染页面。
上一篇我讲了前三步,本篇从第四步开始。下面是正文:
四、进行tcp三次握手,建立tcp连接。
tcp三次握手的细节我已经在之前的文章详细讲过,如果还不清晰的朋友可以移步http你不得不知道的那些事(八)--TCP三次握手。简述一下,第三步我们找到了目标ip,并获得了服务器ip的mac地址。此时浏览器就会请求和服务器连接,用来传输数据。tcp 是稳定双向面向连接的,断开时也会分两边分别断开。面向连接不是说tcp一个双方一直开着的通道,而是维持一个连接的状态,让它看起来有连接。
五、浏览器发送数据,等待服务器响应。
第四步已经建立了连接,此时就要发送数据了。浏览器会对请求进行包装,包装成请求报文。请求报文的格式如下:
起始行:如 GET / HTTP/1.0 (请求的方法 请求的URL 请求所使用的协议)
头部信息:User-Agent Host等成对出现的值
主体
请求头部和主体之间有一个回车换行。如果是get请求,则没有主体部分,post请求有主体部分。当然里面还有些请求头部比较重要,可以参考http你不得不知道的那些事(五)-报文头。
六、服务器处理请求,并对请求做出响应。
浏览器请求报文到达服务器之后,服务器接口会对请求报文进行处理,执行接口对应的代码,处理完成后响应客户端。由于http是无状态的,正常情况下,客户端收到响应后就会直接断开连接,然后一次http事物就完成了。但是http1.0有一个keep-alive的请求字段,可以在一定时间内不断开连接(有时时间甚至很长)。http1.1直接就默认开启了keep-alive选项。这导致了一个后果是服务器已经处理完了请求,但是客户端不会主动断开连接,这就导致服务器资源一直被占用。这时服务器就不得不自己主动断开连接,而主动断开连接的一方会出现TIME_WAIT,占用连接池,这就是产生SYN Flood攻击的原因。
此时有三种处理方式,第一是客户端主动断开连接,第二是服务器主动断开连接,第三是对tcp连接经行设置。第一种情况,如果服务器返回的数据都有确定的content-length属性,或者客户端知道服务器返回的内容终止,则客户端主动断开连接。 第二种情况,服务器可以通过设置一个最大超市时间,可以主动断开tcp连接。第三种情况,调整t三个tcp参数,第一个是:tcp_synack_retries 可以用他来减少重试次数;第二个是:tcp_max_syn_backlog,可以增大syn连接数;第三个是:tcp_abort_on_overflow 处理不过来干脆就直接拒绝连接了。
七、浏览器收到服务器响应,得到html代码。
其实你心里有疑问,这一步有什么好说的。其实这里面有很多需要注意的点。浏览器发出请求时,请求报文如下:
你需要关注一个报文头--accept。accept代表发送端(客户端)希望接受的数据类型,这是浏览器自动封装的请求头。如果服务器返回的content-type是accept中的任何一个,浏览器都能解析,并直接展示在网页上。如果服务器返回的content-type是其他类型,此时浏览器有三种处理状态:
1、正常显示。例如返回类型为text/javascript,浏览器能直接处理并展示。
2、下载。例如返回类型为application/octet-stream(二进制流,不知道下载文件类型),这种浏览器不能直接处理的,会被下载。
3、报错。当我们返回一个字符串hello world,却使用text/xml,格式时,浏览器不能正确解析,就会报错,并把报错信息呈现在网页中。
浏览器能直接处理很多种格式,并直接呈现在网页中,并不限于accept中规定的字段,具体有哪些,就需要自己亲自动手试试了。
附上一张content-type常用对照表地址:http content-type常用对照表
八、渲染页面。
获取到服务器相应之后,浏览器会根据相应的content-type字段对响应字符串进行解析。能够解析并成功解析就显示,能够解析但解析错误就报错,不能解析就下载。由于浏览器采用至上而下的方式解析,所以会先解析html,直到遇到外部样式和外部脚本。这时会阻塞浏览器的解析,外部样式和外部脚本(在没有async、defer属性下)会并行加载,但是外部样式会阻塞外部脚本的执行,dom加载完毕,js脚本执行成功后dom树构建完成(DOMContentLoaded),之后就加载dom中引用的图片等静态资源。(参考文章地址:http://blog.csdn.net/u014168594/article/details/52196460)
即:html解析->外部样式、脚本加载->外部样式执行->外部脚本执行->html继续解析->dom树构建完成->加载图片->页面加载完成。
情况一:如果是动态脚本(即内联脚本)则不受样式影响,在解析到它时会执行。
情况二:外部样式后续外部脚本含有async属性(IE下为defer),外部样式不会阻塞该脚本的加载与执行
在外部样式执行完毕后,css附着于DOM,创建了一个渲染树(渲染树是一些被渲染对象的集)。每个渲染对象都包含了与之对应的计算过样式的DOM对象,对于每个渲染元素来说,位置都经过计算,所以这里被叫做“布局”。然后将“布局”显示在浏览器窗口,称之为“绘制”。
接着脚本的执行完毕后,DOM树构建完成。这时,可以触发DOMContentLoaded事件。DOMContentLoaded事件的触发条件是:在所有的DOM全部加载完毕并且JS加载执行后触发。
情况一:如果脚本是动态加载,则不会影响DOMContentLoaded时间的触发,浏览器会等css加载完成后再加载图片,因为不确定图片的样式会如何。
要点一:CSS样式表会阻塞图片的加载,如果想让图片尽快加载,就不要给图片使用样式,比如宽高采用标签属性即可。
要点二:脚本不会阻塞图片的加载
最后页面加载完成,页面load。
=====end====
这就是在浏览器地址栏输入地址到页面渲染完成的全过程,整个过程比较复杂,涉及到很多东西。
总结一下:运维人员需要处理页面缓存、cdn及keep-alive引起的连接池占用等问题;后端人员需要处理代码逻辑、缓存、传输优化、报错等问题;前段人员需要做好前端性能优化和配合运维、后端做好借口调试,缓存处理等问题。所以无论是前端、后台、运维都应该很清楚整个流程中的每一步,才能在配合时,得心应手,才能在出现问题时,快速准确的定位问题解决问题,才能在需要优化时,迅速完整的给出方案。本篇文章到这里就结束了,如果你发现什么地方有误或不完善,请在评论区留言,thx~。
ps:本篇文章之介绍了http事物,如果是https事物,整个流程和http事物大致相同,唯一不同的就是在http层和tcp层多了一个ssl层,所以在发送数据前会有个ssl握手,发送数据时会有个ssl层的加密。ssl涉及到的东西也不少,例如ssl握手,加密技术,还要ssl层到底在tcp/ip四层协议哪一层的问题等等。我这里只是粗略说说,如果有比较多感兴趣的同学,我可以另外单开一篇,这篇就到这里了。
热门评论
受教了