这是个在面试中可能碰到的问题.
这个问题是比较开放的,根据自身的情况,能说多少是多少,说得越多,说的越细,面试官对你的评价就会越高.当然,前提是不能有什么大错.
该问题涉及多个知识点,并且每个知识点还可以继续深入.
由于每块内容都可以单独拎出来讲,所以后面都不会非常详细的说.
主要知识点
- 不同协议的处理
- url解析
- dns查询
- tcp连接的建立释放过程(3次握手,4次释放)
- tcp/ip模型
- html页面渲染
之后没有特殊说明,将以http协议作为例子.
主要流程
- URL解析.
- dns查询.
- 与服务器建立tcp连接.
- 下载url对应的资源.
- 渲染html页面.
- 释放tcp连接.
详细分析
1. URL解析
URL的标准格式:
协议://<用户名>:<密码>@<主机>:<端口>/<url路径> [?query]#fragment
不同协议,需要有不同的处理方式,当然,因为协议均处于tcp/ip模型的应用层,所以抛开一些展示,效果的不同,底层使用的大同小异.
以 “http://www.baidu.com/hello?name=zhou#1” 作为样例URL.
URL各个参数说明:
|协议|用户名|密码|主机|端口|路径|
|query|fragment|http|www.baidu.com|80|/hello|name=zhou|1|
此处的端口如果没有明确指定,将根据协议(http)的默认端口.
上面就是完整的URL的各部分了.
2. DNS查询
计算机与计算机通讯,需要知道对方的MAC地址.
但是因为MAC地址在早期太长,并且其并非地理上连续的,不利于路由表记录,所以想出利用逻辑地址映射MAC地址来实现路由…
映射关系 IP => MAC
IP地址是由ISP分配的,可以实现物理位置与IP地址一致,并且在实现上利于对路由表进行优化(将成百上千的路由条目优化为少量更大的路由记录,对外暴露).
映射关系 DOMAIN => IP
然而IP地址不利于人记忆且IP地址是可被运营商改变的,对于需要长期稳定对外提供服务的主机来说IP可变将会让其用户大量流失,因此就又对IP地址做了次映射,用域名来隐藏IP,并实现域名对主机的稳定可控关系.
而DNS就是为DOMAIN对IP映射关系记录的服务.
所以知道一个域名地址,要先通过dns查询,获取到IP之后,才能建立连接.
3. 与服务器建立tcp连接
3次握手图示
知道了server的IP地址之后,client就可以请求建立连接.建立tcp连接需要进行3次握手.
为什么握手需要3次,而不是2次,4次,5次?
3次握手的必要性
- client向server发起建立连接的请求.
- server向client发送对"请求1"的确认.
- client向server发送对"请求2"的确认.
对于1,2请求,应该没疑问的,重点在于"请求3",为什么要发送请求3,前2次不久能建立连接了吗?
原因在于,互联网中的所有数据包都存在丢失,延迟的可能,“请求3"是为了保证client确实收到了"请求2”,并且"请求1"是client当前发出的,而不是因为延迟在很久以前发出的.
但这又带来了: 要不要有对"请求3"的确认请求的问题.
由于网络的不可靠,所以要保证消息收到必然要确认机制,但是反复确认会出现确认死循环.
通过定时器避免确认死循环
通过引入多个超时计数器来避免确认的无限循环问题.
说明
MSL
最大分段寿命Maximum Segment Lifetime
TIME_WAIT
表示收到了对方的FIN报文,并发送出了ACK报文,就等2MSL后即可回到CLOSED可用状态了。
FIN_WAIT_1
其实FIN_WAIT_1和FIN_WAIT_2状态的真正含义都是表示等待对方的FIN报文。而这两种状态的区别是:FIN_WAIT_1状态实际上是当SOCKET在ESTABLISHED状态时,它想主动关闭连接,向对方发送了FIN报文,此时该SOCKET即进入到FIN_WAIT_1状态。而当对方回应ACK报文后,则进入到FIN_WAIT_2状态,当然在实际的正常情况下,无论对方何种情况下,都应该马上回应ACK报文,所以FIN_WAIT_1状态一般是比较难见到的,而FIN_WAIT_2状态还有时常常可以用netstat看到。
FIN_WAIT_2
实际上FIN_WAIT_2状态下的SOCKET,表示半连接,也即有一方要求close连接,但另外还告诉对方,我暂时还有点数据需要传送给你,稍后再关闭连接。
CLOSE_WAIT
这种状态的含义其实是表示在等待关闭。当对方close一个SOCKET后发送FIN报文给自己,你系统毫无疑问地会回应一个ACK报文给对方,此时则进入到CLOSE_WAIT状态。接下来呢,实际上你真正需要考虑的事情是察看你是否还有数据发送给对方,如果没有的话,那么你也就可以close这个SOCKET,发送FIN报文给对方,也即关闭连接。所以你在CLOSE_WAIT状态下,需要完成的事情是等待你去关闭连接。
CONNECTION_TIMEOUT
整个tcp连接从client发送第一个SYN包开始计时.
来思考下"请求3"如果丢失,或者重复会是什么样的情况?
丢失:
如果"请求3"丢失,则server将会等待一个 CONNETION_TIMEOUT 时间,如果超时,则取消建立过程.
而client将在TIME_WAIT时间后重新发送携带有ack=y+1 seq的请求.
重复:
对于同一连接过程,重复的"请求3"确认将丢弃.
4.下载url对应的资源
建立tcp连接之后,client和server就能进行应用数据通信了.
请求的是"http://www.baidu.com/hello?name=zhou#1"URL.所以http服务器去超找对应配置.
- 查看是否存在对应主机配置"http://www.baidu.com".
- 如果不存在对应主机配置,则看是否匹配默认配置.
- 找到配置后,再查找是否存在对应资源 “/hello”,此时还要查看该资源是静态资源,还是需要进行转发处理的资源.
- 如果是需要转发处理的请求,则交给对应程序处理.
a. 如果出现错误,则返回对应错误码.
b. 如果成功,则返回对应数据. - 如果是静态资源.
a. 找不到则返回http status: 404.
b. 返回对应资源.
5. 渲染html页面
浏览器由于渲染引擎不同会有不同的渲染过程,下面以webkit引擎说明:
DOM就是Document Object Model.HTML里面的内容都是以成对出现的标签包装起来的,并且只有一个根标签,由这个根开始,其他标签作为节点.由此可以生成一颗DOM树.
DOM树解析完成后,浏览器再去下载其中的资源标签.比如<img>,<style>,<script>
等.
<style>
将去下载css样式文件,之后浏览器对css文件进行解析,再结合DOM树结合,生成渲染树,之后浏览器将渲染树绘制到屏幕上.
<script>
会下载脚本资源或者执行脚本,通常是js,其可能对DOM产生影响.
<img>
不会对DOM产生影响.
之后还要根据脚本的逻辑,进行一些异步或者同步逻辑.
此外,浏览器还会对静态资源进行缓存,以提高对相同资源的多次请求的效率.
6. 释放tcp连接
tcp连接释放需要进行4步.
- client发送FIN请求.
- server发送对"请求1"的确认请求.
- server发送FIN请求.
- client发送对"请求3"的确认请求.
4次释放的必要性
4次释放实质是2次单独的释放及确认过程. 取决于client和server各自的数据收尾情况.
以2人对讲机为例,a和b先互相讲话(互相发送数据),之后a对b表示话讲完了(a数据发送完毕FIN),b表示明白了a没有话要讲了(确认a的FIN),但b没讲完,a是个有礼貌的人,a等b明确表示讲完才会将对讲机收起来,当然之后a已经不再讲话了(发送数据). 然后b也讲完了(发送FIN),a确认了b的结束请求(a确认了b的FIN).
从例子中看到,释放连接之所以要4次,在于双方数据不一致.
有必要对FIN的确认请求再确认吗?
没必要,因为tcp通过超时重传机制来保证这个过程,而避免了无线确认的死循环.