浏览器其实就是一个team,工作很忙很忙,但却井然有序、管理得当,其中既得益于和其他团队的亲密协作,同时也十分注重团队内部每个部门的分工协调。那么这个team日常到底做了哪些工作呢。
各组主要任务安排:
主管老浏今早心情很好,精力也很充沛,昨天甲方正常关闭了浏览器,晚上没有加班。像之前的时候,甲方的电脑经常开一晚上,浏览器也不关,老浏就得几个晚上连续的加班。到了公司,浏览器还没开。等其他伙伴都齐了,时间也刚好,浏览器一开,大家都已经做好了上班的准备。用户界面组,把已经准备好了初始设定好的主题,地址栏,各种标签、菜单、按钮都呈现出来了,渲染引擎组也把预备好的默认页面渲染了出来,第一份任务完成,大伙送了一口气,浏览器初始化ok了。
这会甲方来需求了,“老浏我给你个url,你帮我访问一下,把结果呈现给我”。
老浏把这个url先给了用户界面组,“需要用你们地址栏,检测一下这个url的字符串是不是写的合格”,用户界面组简单分析了一下,没问题。
“数据存储组,你们有这个url的缓存么?”,数据存储组接过ur回库里查了查。”有缓存,他之前访问过,但是缓存已经过期了,重新请求吧“,这会url就到了网络组的手里。
网络组拿到url后,首先进行DNS域名解析工作。组里刚新来一个实习生,问组里的前辈:”哥,为啥咱们要先做DNS域名解析啊“。”你个笨小子,咋学的。因为啊,地址栏里输入的域名和网络服务器的IP地址是有映射关系的,我们得把这个域名解析成IP地址,然后就找到对应的那个服务器了“。实习生又问了:”那具体怎么请求啊?“,”你是真一点不会啊,看你还挺愿意学的,和你简单说说。我们先在本地hosts文件看看有没有这个网址的映射关系,有就直接调用进行解析,没有的话,再看看那本地DNS解析器有没有缓存,没有再看看本地DNS服务器,如果还找不到就得走迭代查询了,一层一层的去找了。“
正在聊天的过程中,旁边的同时来消息了,找到IP了。这会就要进行有名的三次握手过程了,进行TCP链接。网络组组长拿着IP对实习生说:”这个任务咱们有明确的流程规范,你看看然后去和服务器部门的人去对接一下去“。
当建立协议的时候,tcp进行了3次握手:
1、客户端发送了一个带有SYN的Tcp报文到服务器,这个三次握手中的开始。表示客户端想要和服务端建立连
接。主机A发送位码为syn=1,随机产生seq number=1234567的数据包到服务器,主机B由SYN=1知道,
A要求建立联机。
2、服务端接收到客户端的请求,返回客户端报文,这个报文带有SYN和ACK标志,询问客户端是否准备好。
主机B收到请求后要确认联机信息,向A发送ack number=(主机A的seq+1),syn=1,ack=1,随机产生
seq=7654321的包。
3、客户端再次响应服务端一个ACK,表示我已经准备好。主机A收到后检查ack number是否正确,即第一次
发送的seq number+1,以及位码ack是否为1,若正确,主机A会再发送ack number=(主机B的seq+1),
ack=1,主机B收到后确认seq值与ack=1则连接建立成功。
”沟通好了,已经链接了。开始发起HTTP请求,让服务器部那边处理下请求,接受HTTP响应,一定要按照规范传输过来”。
HTTP请求格式:
请求行(requestline) | 请求头部(header) | 空行 | 请求数据 |
常用请求头部:
Accept: 接收类型,表示浏览器支持的MIME类型
(对标服务端返回的Content-Type)
Accept-Encoding:浏览器支持的压缩类型,如gzip等,超出类型不能接收
Content-Type:客户端发送出去实体内容的类型
Cache-Control: 指定请求和响应遵循的缓存机制,如no-cache
If-Modified-Since:对应服务端的Last-Modified,用来匹配看文件是否变动,只能精确到1s之内,http1.0中
Expires:缓存控制,在这个时间内不会请求,直接使用缓存,http1.0,而且是服务端时间
Max-age:代表资源在本地缓存多少秒,有效时间内不会请求,而是使用缓存,http1.1中
If-None-Match:对应服务端的ETag,用来匹配文件内容是否改变(非常精确),http1.1中
Cookie: 有cookie并且同域访问时会自动带上
Connection: 当浏览器与服务器通信时对于长连接如何进行处理,如keep-alive
Host:请求的服务器URL
Origin:最初的请求是从哪里发起的(只会精确到端口),Origin比Referer更尊重隐私
Referer:该页面的来源URL(适用于所有类型的请求,会精确到详细页面地址,csrf拦截常用到这个字段)
User-Agent:用户客户端的一些必要信息,如UA头部等
”叮叮叮~“,”喂,我是服务器部的,你们的HTTP请求已经收到了,我们把报文封装成Request对象,已经处理完了,我们还是返回给你们Response对象,包含状态码,响应头,响应报文就行对吧“。
状态码:
1xx:指示信息--表示请求已接收,继续处理
2xx:成功--表示请求已被成功接收、理解、接受
3xx:重定向--要完成请求必须进行更进一步的操作
4xx:客户端错误--请求有语法错误或请求无法实现
5xx:服务器端错误--服务器未能实现合法的请求
常用响应头部:
Access-Control-Allow-Headers: 服务器端允许的请求Headers
Access-Control-Allow-Methods: 服务器端允许的请求方法
Access-Control-Allow-Origin: 服务器端允许的请求Origin头部(譬如为*)
Content-Type:服务端返回的实体内容的类型
Date:数据从服务器发送的时间
Cache-Control:告诉浏览器或其他客户,什么环境可以安全的缓存文档
Last-Modified:请求资源的最后修改时间
Expires:应该在什么时候认为文档已经过期,从而不再缓存它
Max-age:客户端的本地资源应该缓存多少秒,开启了Cache-Control后有效
ETag:请求变量的实体标签的当前值
Set-Cookie:设置和页面关联的cookie,服务器通过这个头部把cookie传给客户端
Keep-Alive:如果客户端有keep-alive,服务端也会有响应(如timeout=38)
Server:服务器的一些相关信息
ok,网络组,忙忙碌碌的,终于完成了任务,下面就该是渲染的部分了。网络组将服务器返回结果给了渲染引擎组。”大家伙,干得好,再接再厉,准备进入主流程“,老浏鼓励到。
渲染引擎组是一个大组,人数是各组最多的,而且还下分好多小组。渲染引擎组组长收到传来的内容一看,心里一震,这次要渲染的内容很多啊,而且几乎没做多少渲染优化,压力很大啊。”哎,虽然这次任务紧,胆子大,但是什么情况咱们没见过,没有压缩的超级大图,没有逐步渲染的骨架,没有按需加载的模块,咱们都抗过来了,这次也没问题”。组长给全组人不断打气。
第一步,先交给DOM小组,你们把HTML内容翻译一下,构建出出来DOM树。
第二步,交给CSS小组,你们构建一下CSS树,你们和DOM小组可以并行工作。
第三步,布局小组的,你们根据DOM组和CSS组产出的DOM树和CSS树,合并一下,产出一个render树。
第四步,布局小组,你们还需要进行分层工作,生成个分层树。
第五步,每一层都要生成单独的绘制列表,然后发给合成线程组。
第六步,合成线程组会根据用户可见区域将图层划分成图块,只有可见部分才会绘制。
第七步,合成线程小组和栅格化线程池小组,你们两组配合一下,通过你们维护的栅格化的线程池这将视口附近的图块经过栅格化,转换成位图。
第八步,合成线程组将触发DrawQuad绘制图块,通知浏览器进程组。
浏览器线程组获取到各层的信息,便会联系GPU,将各层进行复合操作,显示出来。
“嗯。。。咱们这里需要优化一下,不能等完整的HTML文件都接受完,再一步一步渲染,应该在接受部分的时候就开始先渲染。”
渲染引擎组还有两个最重要的工作,一个是reflow(回流),一个是回流回流repaint(重绘)。回流指的是元素的内容、结构、位置或尺寸发送变化,需要重新计算样式和布局。重绘指的是元素的变化仅仅是一些不影响布局和结构的属性时,触发的操作。等全部渲染完后,若出现布局变化,会重现触发回流重绘的步骤。
信息接受完了,准备关闭TCP链接,进行四次挥手操作。
当终止协议的时候,tcp进行了4次握手: 由于Tcp连接是进行全双工工作的,因此每个方向都必须单独进行关
闭,这个原则是当一方完成他的数据发送的 时候就发送一个FIN来终止这个方向的连接,收到这个FIN意味着这
个方向上没有数据的流动,一个TCP连接在 收到这个FIN之后还能发送消息,首先执行关闭的一方进行主动的关
闭,而另一方进行被动的关闭。
1、TCP发送一个FIN,用来关闭客户到服务端的连接。
2、服务端收到这个FIN,他发回一个ACK,确认收到序号为收到序号+1,和SYN一样,一个FIN将占用一个序号。
3、服务端发送一个FIN到客户端,服务端关闭客户端的连接。
4、客户端发送ACK报文确认,并将确认的序号 +1,这样关闭完成。
“完美,我们又完成了一次伟大的胜利,没有阻塞,没有延迟,不过之后我们还是需要做下复盘,下次怎么能更快,有哪些需要优化的部分”。老浏流露出喜悦的表情。
整体工作流程