前两天面试,面试官问了我一个问题,刨根到底的问,把我问的快哭了,事后回来赶紧补功课!
面试官:前后端是怎么通信的
我: 通过http发送一个请求,根据请求的地址,找到对应的SQL,返回最终的数据
面试官:那是怎么找到对应的SQL的?
我:
1. 看有没有缓存,如果有缓存就走缓存中的数据,如果没有缓存就开始解析http地址
2. 输入url通过DNS解析为对应的IP地址,找到对应的服务器
3. 建立TCP链接,传送数据
面试官:TCP链接过程是怎么样的?,有几层传输协议?
我:TCP传输涉及到3次握手,4次挥手
面试官:那你讲下3次握手,4次挥手吧。
我:3次握手是发送请求时候,挥手是结束请求时候
发送请求:客户端像服务端发送请求时候,先向服务端发一条请求告诉服务端要请求了,服务端收到请求后,告诉客户端,请求收到了,可以发了,客户端发请求
终止请求:客户端向服务端发请求,说我要终止请求了,服务端收到请求后,告诉客户端,我收到请求了,但是,我这边还有事没处理完,等我处理完再告诉你,服务端处理完事情后,告诉客户端说我这边处理完了,可以关闭请求了,客户端发起关闭请求
面试官:你简单的介绍了下TCP连接的过程,那里面涉及到哪里协议,你讲下
我:~~~ 我不干了,谁要谁回答!!!
事后我回来仔细的巴了巴相关的知识,主要还是集中体现在输入URL之后到底发生了什么?
这里忽略缓存解析,直接是首次进行解析
URL解析过程
1. 域名解析:
一般我们记录网站都会记住名字,没有人就记录一个IP地址,那么这个过程就是DNS解析,主要是域名到IP地址之间的解析服务
DNS 服务器是高可用、高并发和分布式的,它是树状结构,如下图所示:
DNS解析过程:
输入URL后,
先在浏览器缓存中查找 --> 本地hosts文件中查找 -->本地DNS解析器缓存查找 -->本地服务器查找
本地域名服务器的 DNS 缓存没有命中,则本地域名服务器向上级域名服务器进行迭代查询
1. 向根域名服务器发请求,拿到顶级域名服务器的地址
2. 再向顶级域名服务器发请求,拿到权限域名服务器地址
3. 向权限域名服务器发请求,拿到该域名对应的IP地址
这一过程是一个迭代查找的过程,如下图所示:
域名查找分为2种:
递归查找:A向B发请求如果B不知道请求的内容,则继续向上发送请求,直到获取所需的内容,再返回给A
迭代查找: A向B发送请求,如果B不知道请求内容,则告诉A如何获取内容,让A继续去请求
2. 建立TCP连接
找了服务器地址后,要跟服务器进行通信连接,能够获取到前端打包之后的代码、数据等
两个进程之间要通信就必须建立一个连接,通过这个连接来相互传递数据。网络的通信协议一般是采用TCP协议,这个协议在读写之前必须建立一个连接,客户端和服务端用来收发数据,当读写操作完成之后,就必须释放这个连接。连接的建立需要3次握手,连接的释放需要4次挥手,所以说每个连接的建立是非常消耗资源时间的。
TCP位于传输层, 提供可靠的字节流服务。所谓的字节流服务(Byte Stream Service) 是指, 为了方便传输, 将大块数据分割成以报文段(segment) 为单位的数据包进行管理。 而可靠的传输服务是指, 能够把数据准确可靠地传给对方。 即TCP 协议为了更容易传送大数据才把数据分割, 而且 TCP 协议能够确认数据最终是否送达到对方。所以,TCP连接相当于两根管道(一个用于服务器到客户端,一个用于客户端到服务器),管道里面数据传输是通过字节码传输,传输是有序的,每个字节都是一个一个来传输。
TCP/IP 协议体四层结构:
1、物理层:虽然链路层的作用是将帧从一个端系统运输到另一个端系统,而物理层的作用是将帧中的一个个 比特
从一个节点运输到另一个节点,物理层的协议仍然使用链路层协议,这些协议与实际的物理传输介质有关,例如,以太网有很多物理层协议:关于双绞铜线、关于同轴电缆、关于光纤等
2、数据链路层:现在我们有应用程序通信的协议,有了给应用程序提供运输的协议,还有了用于约定发送位置的 IP 协议,那么如何才能真正的发送数据呢?为了将分组从一个节点(主机或路由器)运输到另一个节点,网络层必须依靠链路层提供服务。链路层的例子包括以太网、WiFi 和电缆接入的 DOCSIS
协议,因为数据从源目的地传送通常需要经过几条链路,一个数据包可能被沿途不同的链路层协议处理,我们把链路层的分组称为 帧(frame)
对应设备:网线、网桥、集线器、交换机
3、网络层:负责将称为 数据报(datagram)的网络分层从一台主机移动到另一台主机。网络层一个非常重要的协议是 IP
协议,有具有网络层的因特网组件都必须运行 IP 协议。是为了实现数据包的选路和转发,IP协议增加作为通信目的地的MAC地址后转发给链路层
对应设备:路由器
4、传输层:为两台主机上的应用程序提供端到端的通信。与网络层使用的逐跳通信方式不同,
传输层只关心通信的起始端和目的端,而不在乎数据包的中转过程.。TCP协议为了传输方便,将HTTP请求报文进行分割,并在各个报文上打上标记序号及端口号后转发给网络层。
5、应用层:负责处理应用程序的逻辑。是网络应用程序喝网络协议存放的分层,应用层协议分布在多个端系统上,一个端系统应用程序与另外一个端系统应用程序交换信息分组,我们把位于应用层的信息分组称为报文
OSI 模型
我们上面讨论的计算网络协议模型不是唯一的 协议栈
,ISO(国际标准化组织)提出来计算机网络应该按照7层来组织,那么7层网络协议栈与5层的区别在哪里?
从图中可以一眼看出,OSI 要比上面的网络模型多了 表示层
和 会话层
,其他层基本一致。表示层主要包括数据压缩和数据加密以及数据描述,数据描述使得应用程序不必担心计算机内部存储格式的问题,而会话层提供了数据交换的定界和同步功能,包括建立检查点和恢复方案
TCP协议传输的过程中还涉及到“三次握手,四次挥手”
三次握手:握手过程中使用了TCP标志——SYN(synchronize)和ACK(acknowledgement)
第一次握手:建立连接,客户端A向服务端发送SYN包(SYN=j),同时选择一个初始序列号 seq = x ,并进入SYN_SEND状态,等待服务器B确认。TCP规定,SYN报文段(SYN=1的报文段)不能携带数据,但需要消耗掉一个序号
第二次握手:服务端收到SYN包,先确认客户端A的SYN包(ACK=j+1),同时自己也发送一个SYN包(SYN=K),即SYN+ACK包。确认号是ack=x+1,同时也要为自己初始化一个序列号 seq=y。此时服务器B进入SYN_RECV状态。这个报文也不能携带数据,但是同样要消耗一个序号
第三次握手:客户端A收到服务端SYN+ACK包,向服务器B发送确认ACK(ACK=K+1),ack=y+1,自己的序列号seq=x+1。此时,TCP连接建立,客户端进入ESTABLISHED(已建立连接)状态。TCP规定,ACK报文段可以携带数据,但是如果不携带数据则不消耗序号。此次包发送完毕,代表“握手结束”
为什么TCP客户端最后还要发送一次确认呢?
一句话,主要防止已经失效的连接请求报文突然又传送到了服务器,从而产生错误。
如果使用的是两次握手建立连接,假设有这样一种场景,客户端发送了第一个请求连接并且没有丢失,只是因为在网络结点中滞留的时间太长了,由于TCP的客户端迟迟没有收到确认报文,以为服务器没有收到,此时重新向服务器发送这条报文,此后客户端和服务器经过两次握手完成连接,传输数据,然后关闭连接。此时此前滞留的那一次请求连接,网络通畅了到达了服务器,这个报文本该是失效的,但是,两次握手的机制将会让客户端和服务器再次建立连接,这将导致不必要的错误和资源的浪费。
如果采用的是三次握手,就算是那一次失效的报文传送过来了,服务端接受到了那条失效报文并且回复了确认报文,但是客户端不会再次发出确认。由于服务器收不到确认,就知道客户端并没有请求连接。
四次挥手:由于TCP连接是全双工的,因此每个方向都必须单独进行关闭。这个原则是当一方完成它的数据发送任务后就能发送一个FIN来终止这个方向的连接。收到一个 FIN只意味着这一方向上没有数据流动,一个TCP连接在收到一个FIN后仍能发送数据。先进行关闭的一方将执行主动关闭,而另一方被动关闭
1. 客户端(Client)发送中断连接请求,发送FIN报文,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。
2. 服务端(Server)接收到FIN报文后,向客户端发送确认报文ACK=1ack=u+1,并且带上自己的序列号seq=v 。此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间(反馈客户端信息,你的请求我收到了,但是我还没有准备好,请等待我的消息)
3. 客户端收到服务器的确认请求后,此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。
4. 当服务端确定数据已发送完毕,就向客户端发送连接释放报文。向客户端发送FIN=1,ack=u+1报文,由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认(告诉客户端,我这边数据发送完毕,可以关闭连接)
5. 客户端收到信息后,就知道可以关闭连接了,但它不相信网络,怕服务端不知道要关闭,所以发送ACK=1报文,ack=w+1,而自己的序列号是seq=u+1,尔后进入TIME_WAIT状态,注意此时TCP连接还没有释放,必须经过2∗ *∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态
6. 服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。
三次握手和四次挥手:在TCP连接中,服务器端的SYN和ACK向客户端发送是一次性发送的,而在断开连接的过程中, B端向A
端发送的ACK和FIN是分两次发送的。因为在B端接收到A端的FIN后, B端可能还有数据要传输,所以先发送ACK,等B端处理完自己的事情后就可以发送FIN断开连接了
为什么客户端最后还要等待2MSL?
MSL(Maximum Segment Lifetime),TCP允许不同的实现可以设置不同的MSL值。
第一,保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。
第二,防止类似与“三次握手”中提到了的“已经失效的连接请求报文段”出现在本连接中。客户端发送完最后一个确认报文后,在这个2MSL时间中,就可以使本连接持续的时间内所产生的所有报文段都从网络中消失。这样新的连接中不会出现旧连接的请求报文
为什么建立连接是三次握手,关闭连接确是四次挥手呢?
建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。
而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。
如果已经建立了连接,但是客户端突然出现故障了怎么办?
TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。
UDP协议:
无连接协议,也称透明协议,也位于传输层
两者区别:
1) TCP提供面向连接的传输,通信前要先建立连接(三次握手机制); UDP提供无连接的传输,通信前不需要建立连接。
2) TCP提供可靠的传输(有序,无差错,不丢失,不重复); UDP提供不可靠的传输。
3) TCP面向字节流的传输,因此它能将信息分割成组,并在接收端将其重组; UDP是面向数据报的传输,没有分组开销。
4) TCP提供拥塞控制和流量控制机制; UDP不提供拥塞控制和流量控制机制
在网络通信的过程中,主机与主机的连接是通过TCP协议进行连接的,既然是连接,就分为长连接和短连接
短连接:短时间的连接
长连接:长时间的连接
短连接优点:
1、管理简单,不需要操心连接状态的管理
2、由于每次使用的连接都是新建的,所以基本上只要能够建立连接,数据就大概率能送达到对方。并且哪怕这次传输出现异常也不用担心影响后续新的数据传输,因为届时又是一个新的连接
短连接的缺点:
如果客户端连接频繁,会在tcp的建立和关闭上浪费时间和带宽
长连接优点:
1、省时间,在多次通信中可以避免多次的连接,从而节省了连接建立和断开的时间并且从总体上来看,进行多次数据传输的总耗时更少,减少网络阻塞的影响
2、当发生错误时,可以在不关闭连接的情况下进行提示
3、节约资源:减少CPU及内存的使用,因为不需要经常的建立及关闭连接。
长连接缺点:
1、连接数过多时,影响服务端的性能和并发数量
2、我们就需要担心各种问题:比如端对端连接的维护,连接的保活等。需要花费额外的精力来保持这个连接一直是可用的,因为网络抖动、服务器故障等都会导致这个连接不可用,甚至是由于防火墙的原因
在长连接的过程中,由于是长时间的连接,对于服务器来说就需要判断这个连接是在连接中还是断开了,所以需要一个机制来判断连接的状态,这个机制就是心跳机制
心跳:像人的心跳一样,人的心跳是用来检测生命的,通信中的心跳是用来检测一个系统是否存活或网络链路是否通畅的一种方式,人的生命是看心跳是否在跳动,而通信中的心跳是每隔固定的时间就向被检测的系统发送心跳包,被检测系统对收到的心跳包进行回复,收到回复说明对方存活,心跳能够给长连接提供保活功能,能够检测长连接是否正常
心跳包:在TCP的机制里面,本身是存在有心跳包的机制的,也就是TCP的选项:SO_KEEPALIVE,系统默认是设置的2小时的心跳频率。但是它检查不到机器断电、网线拔出、防火墙这些断线。心跳包一般来说都是在逻辑层发送空的echo包来实现的。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。其实,要判定掉线,只需要send或者recv一下,如果结果为零,则为掉线。但是,在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是难以知道的。更要命的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉,在这个时候,就需要我们的心跳包了,用于维持长连接,保活。在获知了断线之后,服务器逻辑可能需要做一些事情,比如断线后的数据清理呀,重新连接呀……当然,这个自然是要由逻辑层根据需求去做了。
一般的应用下,心跳包的时间判定在30-40秒比较不错。如果实在要求高,那就在6-9秒
心跳检测步骤:
1 客户端每隔一个时间间隔发生一个探测包给服务器
2 客户端发包时启动一个超时定时器
3 服务器端接收到检测包,应该回应一个包
4 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
5 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
就像人的心跳一样,每跳动一次就发送一次消息,看这个消息有没有回应,如果有回应就说明这个长连接还在连接中,如果没有回应就说明连接断开了。
3. 浏览器渲染解析
拿到服务端返回的数据之后,接下来就是浏览器解析的过程了
浏览器解析的过程如下:
1. 浏览器对HTML文档进行解析并构建DOM树
2. 遇到CSS样式,如外链的css样式或style标签内的css或style属性内嵌到额css,就开始解析样式表,并构建样式树(样式树和DOM树是相互独立,同时进行解析的)
3. 遇到sript标签,就停止DOM解析并开始下载和解析js
4. 解析完DOM树和样式树之后,就开始进行渲染DOM树
5. DOM树渲染完成之后,就开始进行页面布局
6、遍历DOM树,绘制页面
4-6,浏览器开始渲染DOM树并将其绘制到屏幕上面的时候,这个过程比较复杂,会涉及到2个概念,重绘(repain)和重排(reflow)也叫回流,DOM中的各个节点都是以盒模型的形式存在的,这些盒模型都需要去计算其位置和大小,这个过程就叫重排,当盒子的位置和大小以及其他的属性确定之后,如颜色、字体等,浏览器便开始绘制内容,这个过程叫重绘。页面在首次加载的时候必定会经历重绘重排,这是非常消耗性能的,考虑到性能问题,我们一般要减少页面的重绘重排。
参考资料: