主要步骤如下:
- 1、URL解析
- 2、DNS查询
- 3、发送HTTP请求,建立TCP连接
- 4、服务器处理请求并响应
- 5、浏览器接收响应,解析,渲染到页面
1 URL解析
URL(Universal Resource Locator) 统一资源定位符,用来表示某个资源的地址,其组成部分如下:
-
- 传输协议 (如https)
-
- 域名
-
- 端口(默认不显示,跟在域名后面,以:开头,HTTP默认是80,HTTPS默认是443)
-
- 虚拟目录
-
- 文件名
-
- 参数(从?开始,止于#,多个参数用&连接)
-
- 锚 (在#后面)
比如bilibili主页https://www.bilibili.com/anime/?spm_id_from=333.851.b_696e7465726e6174696f6e616c486561646572.2
传输协议为https , 域名是www.bilibili.com , 然后是虚拟目录/anime/,之后跟了一个spm_id_from的参数
解析url作甚
- 字符编码
- 补全:比如输入bilibili.com回车后会变成 www.bilibili.com
- HSTS: 由于安全隐患,会使用HSTS强制客户端使用HTTPS访问页面,比如输入http://bilibili.com 回车后会变成https://bilibili.com
检查缓存
- 存在缓存
- 没有过期,读取缓存返回缓存
- 过期了,携带标识向服务器请求是否有更新
- 没有更新,返回304,继续使用缓存,读取缓存返回缓存
- 更新了,重写返回资源和缓存标识,返回200,存入本地,加载页面
- 没有缓存,向服务器请求,返回请求结果和缓存标识,存入本地,加载页面
2 DNS查询
DNS(Domain Name System):域名系统
域名是对于于IP地址的映射,因为IP地址不好记,而域名相对更好记,但数据报又不能依靠域名进行传播,因为IP地址长度固定(ipv4 32位 ipv6 128位),但域名长度不固定,处理起来比较困难,所以需要将域名转化为IP地址进行主机间的通信。
互联网的域名系统DNS设计成为一个联机分布式数据库系统,并采用客户服务器方式。DNS使大多数的名字都在本地进行解析,仅少量的解析需要在互联网上通信,因此DNS的效率很高,由于DNS是分布式的,即使单个计算机出了问题,也不会妨碍整个DNS的正常运行。
域名服务器程序:完成域名到IP地址的解析的程序
域名服务器:运行域名服务器程序的机器
域名:由标号序列组成,各个标号之间由“ . ”隔开。
- 标号都由英文字母和数字组成,以及连字符“-”
- 每个标号不能超过63个字符(为了记忆最好不要超过12个)
- 不区分大小写
- 级别最低的写在最左边,级别最高的顶级域名写在最右边,级别递增
- 多个标号组成的域名长度不能超过255个字符
- DNS不规定域名的级数多少个,不规定域名的每一级各代表什么意思
2.1 域名级别分类
分为从高级到低级,从右边到左边,分为顶级域名、二级域名、三级域名、四级域名... 前面也提到DNS对于域名级数和每一级域名的具体含义不做限制,所以域名是相对自由的,一旦某个单位拥有了一个域名,它可以自行决定是否要划分下面的子域并不需要上级机构的批准,一些比较普遍的可以参考下面的思维导图。
域名系统可以表示为一棵树,最上面是根,根下面是顶级域名,顶级域名下面是各自的二级域名,二级域名下又是三级域名,三级域名下又是四级域名...是按照组织机构划分的,和地区无关
2.2 域名服务器
可以按照域名系统的树形结构,给每一级的域名都设置一个相应的域名服务器,但这样会让域名服务器的数量过多,使域名的运行效率更低,所以提出了划分区的方式,一个服务器管理的范围叫做区,可以对应域名系统的一棵子树,区内所有节点是连通的。
DNS服务器以区为单位管辖,区小于等于域,不能大于域
根据域名服务器的作用,可以把域名服务器分为:
- 根域名服务器:最高层次的域名服务器,知道所有的顶级域名服务器的域名和IP地址
- 顶级域名服务器:管理在该顶级域名服务器下注册的所有二级域名
- 权限域名服务器:负责一个区的域名服务器
- 本地域名服务器:离用户较近,当一台主机发出DNS查询请求,最先发送给本地域名服务器,如果被查询的主机也属于同一个本地ISP,该本地域名服务器能够立即将查询到的主机名称转为IP地址
2.3 DNS查询方式
- 主机向本地域名服务的查询一般是递归查询。如果本地域名服务器不知道被查询域名的IP,本地域名服务器就说,包在我身上,我查到了告诉你,然后本地域名服务器就会自己去问其他根域名服务器,该主机等着结果就可以。递归查询返回的结果要么是IP地址,要么是报错说没有查到
- 本地域名服务器向根域名服务器查询通常采用迭代查询
- 根域名服务器收到本地域名服务器的迭代查询请求报文,要么直接给出要查询的IP地址,要么告诉本地域名服务器后续查询哪个顶级域名服务器(IP地址)。
- 顶级域名服务器收到本地域名服务器查询请求,要么直接给出查询的IP地址,要么告诉本地域名服务器下一步查询哪个权限域名服务器(IP地址)
- 就这样一步一步,本地域名服务器自己查询到了主机请求的IP地址,返回给主机即可
注意
- 主机向本地域名服务器查询一般是用递归查询 ,而本地域名服务器怎么查和其没有关系
- 本地域名服务器向根域名服务器的查询,一般是用迭代查询 ,也可以用递归查询,取决于一开始的设置,两种方式耗费的UDP报文数量相同
高速缓存
为提高查询效率,减轻根域名服务器的负荷和减少互联网上DNS查询报文数量,在域名服务器当中广泛用到了高速缓存,缓存最近查询过的域名以及从何处获得域名映射的信息记录。比如要查询域名y.abc.com,而本地域名服务器中有这个查询记录,那么直接告诉本机其IP地址,不用重新查询。由于域名的时效性,要设置一定的间隔,将过期的项目丢弃。
主机中也会设有高速缓存,最开始从本地域名服务器当中下载,然后自己的每次查询都会记录到本地高速缓存,自己进行维护,以此减少DNS查询次数
2.4 DNS查询步骤总结
- 查询浏览器缓存
- 检查主机中高速缓存是否存在该域名的IP地址,有则不必进行DNS查询
- 检查路由器缓存
- 向本地域名服务器进行递归查询
- 本地域名服务器检查自己的高速缓存当中是否存在该域名的IP,有则直接返回给主机
- 本地域名服务器的高速缓存中没有,本地域名服务器向根域名服务器迭代查询(或者递归查询),将查询结果返回给主机(返回IP地址或者没有查到)
3 TCP连接
TCP的主要特点如下:
- TCP是面向连接的运输层协议,使用TCP协议之前,必须先建立TCP连接,传输数据完成之后,必须释放已经建立的连接
- 每一条TCP连接只能有两个端点。TCP把连接作为最基本的抽象,每一条TCP连接唯一地被通信两端的两个端点(套接字)所确定,TCP连接::={socket1,socket2}={(IP1:port),(IP2:port)}
- TCP提供可靠交付的服务。通过TCP连接传送的数据,无差错、不丢失、不重复,并且按序到达
- TCP提供全双工通信。TCP允许通信的双方的应用程序在任何时候都能发送数据。
- TCP是面向字节流的。TCP把应用程序交下来的数据仅仅看作一连串的无结构的字节流(流入进程或者从就进程流出的字节序列),TCP不知道传送的字节流的含义。
在TCP/IP体系结构当中,发送方的数据每经过一层就会进行一次封装,接收方收到数据后每经过一层就会进行一次拆分。
- 应用层 HTTP数据
- 传输层 TCP首部 + HTTP数据
- 网络层 IP首部 + TCP首部 + HTTP数据
- 网络接口层:以太网首部 + IP首部 + TCP首部 + HTTP数据
3.1 TCP连接的建立——三报文握手
3.1.1 相关字段
在了解TCP连接的建立之前,需要认识一下相关字段,TCP报文段首部是固定20个字节+可选4n字节
- 序号:seq,也叫报文段序号,占4个字节,范围是[0,2^32 - 1],采用mod2^32运算,超过2^32-1回到0,是对于TCP连接中传送的字节流的每个字节的按顺序的编号,起始序号在连接建立的时候设置
- 确认号:ack,占4个字节,期望收到的下一个报文段的第一个数据字节的序号,若确认号为N,则表明到序号N-1的数据都已经正确收到
- 确认ACK:(ACKnowledgment)占1位,仅当ACK设置为1时确认号才是有效的,在连接建立之后所有的报文的ACK都置为1
- 同步SYN: (SYNchronization)占1位,在连接建立的时候用来同步信号
- SYN = 1,ACK = 0,表明这是一个连接请求报文段
- SYN = 1,ACK = 1,表明对方接受建立连接
3.1.2 建立过程
假设现在存在客户端A,服务器B,两者都处于CLOSED状态,则建立AB之间建立TCP连接的步骤如下:
- B创建传输控制块TCB,准备接受客户进程的连接请求,然后B处于LISTEN状态,等待客户的连接请求
- A创建传输控制块TCB,打算和B建立TCP连接,向B发送请求报文段
- 首部当中SYN = 1,选择一个序号seq = x
- 这个报文段不携带数据但是消耗一个序号
- 发送这个报文段后A进入SYN-SENT(同步已发送)状态
- B收到连接请求报文段后,如果同意建立连接,则向A发送确认
- 报文段当中SYN和ACK都置为1
- ack = x + 1(确认收到序号在x + 1前A的报文,希望收到下一个报文的序号是x + 1)
- 也为自己确定一个初始序号seq=y
- 这个报文段同样不携带数据,但是消耗一个序号。
- 发送确认报文之后之后B进入SYN-RCVD(同步收到)的状态
- A收到B的确认之后,还要向B给出确认
- 确认报文段的ACK置为1
- 确认号ack = y + 1 (确认收到B的序号在y + 1之前的报文,希望下一个报文序号是y + 1)
- 序号seq = x + 1 (A的请求连接报文seq = x 消耗了一个序号)
- 这个报文可以携带数据也可以不携带,不携带数据时,seq = x + 1不会被消耗
- A发送完这个确认后进入ESTABLISHED(已建立连接)的状态
- B收到A的确认之后也会进入ESTABLISHED状态
为什么A确认B的同意建立连接的报文之后还要回一个确认?
为了防止已经失效的连接请求报文段突然传送到了B,从而产生错误。
情景假设正常情况:
- A发送连接请求,报文由于一些原因没有送到B
- B没有收到也就没有确认建立连接,A不会收到B的确认
- A重新发送连接请求
- B收到了,向A发送确认
- A收到了B的确认,向B发送确认,两者建立连接
- 建立连接完成数据传输断开连接
- 之前丢失的A的请求建立连接的报文送到了B
- B向A确认建立连接
- A此时不回复B,表示这个请求是乌龙
- B没有收到A的确认,这个连接不会建立
假如TCP连接的建立只需要A请求B,B向A确认,上面的情况就会变成丢失的请求也让AB之间建立了TCP连接,A对此不知情,B也傻傻地等A传数据过来,为A保持这个TCP连接,B的资源就被浪费了
3.2 TCP连接的释放—— 4次挥手
终止FIN: 占1位,用来释放一个连接。FIN = 1表示报文的发送方的数据已经发送完毕,要求释放运输连接
还是客户端A与服务器B,A和B建立连接之后都处于ESTABLISHED的状态,A完成数据传输之后想要断开连接,过程如下:
- A向B发送连接释放报文段,并停止向B发送数据
- FIN 置为1,表明是释放连接的报文
- seq置为u,u为A前面已经传送过的最后一个字节的序号 + 1
- FIN报文即使不携带数据也消耗一个序号
- A 进入 FIN-WAIT-1(终止等待1)状态
- B收到A的连接释放的报文,向A进行确认
- ACK置为1
- ack = u + 1 (确认收到A的seq = u)
- seq = v, v为B前面已经传送的最后一个字节的序号 + 1
- B 进入 CLOSE-WAIT (等待关闭)状态
- A没有数据要发送给B , 但B可以发送一些数据给A,TCP连接处于半关闭状态
- A收到B的确认,进入FIN-WAIT-2的状态,等待B把其剩余数据传输完后发送连接释放报文段
- B所有数据报文段发送完,向A发送连接释放报文段
- FIN 置为1,表示B要和A一刀两段
- ACK置为1,表示确认号有效
- ack = u + 1 (A没有对B再发送报文段,所以还是原来的确认号)
- seq = w (B在确认A的连接释放报文之后又发送了一些数据,最后一个字节 + 1 = w)
- B 进入LAST-ACK(最后确认)状态
- A收到B的连接释放报文,向B确认
- ACK= 1
- ack = w + 1
- seq = u + 1
- A 进入TIME-WAIT(时间等待状态),经过时间等待器设置的时间2MSL后,A进入CLOSED状态
- B收到来自A的确认,进入CLOSED状态
可见B会稍稍早一些比A进入CLOSED状态。除了时间等待计时器,TCP还设计了保活计时器。为了防止客户端机器故障,不再传输数据但却占用连接的情况。服务器每收到一次客户端的数据,就重置保活计时器(通常2h),如果经过这段时间服务器都没有收到数据,服务器就会发送探测报文给客户端,75秒一次,一连10次都没反应,服务器判断客户端出问题,关闭连接。所以有要保持长连接客户端定时发送心跳包给服务器的情况。
为什么A在收到B的连接释放报文并确认后,还要等待2MSL的时间?
MSL(maximum Segment Lifetime),最长报文段寿命,RFC793规定为2min,但实际可根据具体情况设置为更小值
- 为了让A发送给B的最后一个ACK能够到达B,如果这个确认丢失,B无法进入CLOSED状态
- A发送给B的确认报文段丢失,处于LAST-ACK状态的B收不到A的确认
- 经过MSL,B等的不耐烦了,B向A重新发送FIN + ACK报文段
- 又经过MSL,A会收到B的重传,A也明白自己的确认丢了
- A向B重新发送确认,之后又是重复昨天的故事,2MSL计时器重置
- 防止已经失效的连接请求报文,和上面提到的情况一样,A发送确认报文后,经过2MSL,本连接持续时间内产生的所有报文都已经从网络消失。下次建立新的连接的时候,就不会有旧的连接请求报文段
4. HTTP请求与响应
注意,是先有HTTP请求,之后才有了TCP连接。
在DNS得到目标的IP地址后,浏览器会开始构造HTTP报文,包括:
- 请求头(Request Header):请求方法,目标地址,遵循的协议等;浏览器只能发送GET、POST方法,打开网页使用的是GET方法
- 请求主体
在有了HTTP报文后,向下层层封装后进行发送,服务器收到后向上层层拆分,然后进行响应
HTTTP请求方式种类(action)
- HTTP/0.9中只能用GET
- HTTP/1.0中定义了3种请求方式:GET、POST和HEAD方法
- HTTP/1.1中新增了5种请求方法:OPTIONS、PUT、DELETE、TRACE和CONNECT方法
各种方式的特点如下:
- GET:向特定的资源发出请求
- POST:向指定资源提交数据进行处理请求,POST数据被包含在请求体当中,POST请求可能会导致新的资源的建立或者已有资源的修改
- HEAD: 和GET请求类似,不过返回的响应当中没有具体内容,用于获取头部
- OPTIONS: 测试服务器的性能
- PUT: 向指定资源位置上传最新内容
- DELETE: 请求服务器删除所标识的资源
- TRACE: 回显服务器收到的请求,用于测试
- CONNECT: 让服务器代替主机去访问其他网页然后返回数据
服务器处理请求
- 监听到HTTP请求之后,开启一个子进程处理请求
- 对HTTP请求进行解析(请求方法、域名、路径)、验证(是否配置虚拟主机、虚拟主机是否接收此方法、该用户使用该方法的权限1)
- 判断是否重定向,是则返回301,浏览器收到后根据响应重新发送HTTP请求
- URL重写
- 如果请求文件真实存在,直接返回文件
- 否则服务器按照规则把请求重写到REST风格的URL上,根据动态语言的脚本决定调用相应类型的动态解释器来处理请求
5 浏览器接收响应,解析,渲染页面
HTTP协议响应消息常用状态码 实在是有点多(100-101、200-206、300-307、400-417、500-505),挑了一些不熟悉这方面也能看到的状态码
- 200:OK 成功
- 400:BadRequest 错误请求
- 401:Unauthorized 未授权
- 403:Forbidden 已禁止 资源不可用
- 404:Not Found 未找到 没有找到指定位置的资源
- 500:Internal Server Error 服务器内部错误
- 502:Bad Gateway 错误网关,服务器为了完成请求访问下一个服务器,被返回非法的应答
浏览器收到响应资源后对响应资源做分析,根据响应头状态码做相应操作,如果资源压缩,需要进行解压,之后将资源缓存,之后根据资源MIME类型去解析响应内容。
解析之后将内容渲染到页面上,渲染会分为HTML、Style、Script三部分,各个浏览器内核渲染过程大同小异。
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!