基石-初见网络(一):输入URL后

前言

记录本身第一次学计算机网路基础后的总结,但愿各位大佬可以帮助小老弟纠正错误,以及对知识点进行补充。php

以一道经典面试题 输入URL后发生了什么 为核心,开始了我学习计算机网络之旅。本篇文章知识对部分过程进行一个总结概述,其中遇到的有些知识点因为太深刻没有学习到,通过后续学习以后会对本文进行补充整理。前端

但愿本文可以帮各位大佬回顾一下计网的基础知识点,同时若是你是和我同样在前端路上刚开始出发,能够点赞+关注,以后会陆续分享我整理好的知识体系。 面试

尾部有彩蛋哈~

概述过程

分为两个大部分,一部分是生成HTTP请求,另一部分是浏览器委托协议栈进行通讯。浏览器

大致分为以下步骤:缓存

  1. 解析url
  2. 生成HTTP请求
  3. 向DNS服务器查询WEB服务器的IP地址
  4. 全世界DNS服务器的接力
  5. 委托协议栈发送信息
  6. 建立套接字
  7. 链接服务器
  8. 收发数据
  9. 断开链接

因为知识点有点多,请各位大佬细听小弟娓娓道来。服务器

生成HTTP请求

解析url

咱们的探索之旅从在浏览器中输入网址开始,在介绍浏览器的工做方式以前,让咱们先来介绍一下网址,准确来讲应该叫URL。markdown

实际上除了“http:”,网址还能够以其余一些文字开头,例如 “ftp:” “file:” “mailto:”等。之因此有各类各样的URL,是由于一般浏览器是用来访问Web服务器的,但它也能够用来在FTP服务器上下载和上传文件,同时也具有电子邮件客户端的功能。网络

能够说浏览器是一个具有多种客户端功能的综合性客户端软件并发

所以它须要一些东西来判断应该使用其中哪一种功能来访问相应的数据,而各类不一样的URL就是用来肯定网址所使用的不一样方法,一般咱们称之为协议类型socket

因此说浏览器要作的第一步工做就是对URL进行解析

HTTP

解析完URL以后,咱们就知道应该要访问的目标在哪里了。接下来,浏览器会使用HTTP协议来访问Web服务器,在此以前咱们先复习一下HTTP协议。

HTTP协议定义了客户端和服务器之间交互的消息内容步骤

首先,客户端会向服务器发送请求消息,请求消息中包含的内容是对谁进行何种操做两个部分。

  • 其中对谁的部分称为URI,这里能够写各类访问目标,而这些访问目标统称为URI(统一资源标识符)。
  • 另外一部分称之为方法,表示须要让Web服务器完成怎样的工做。经过下面这张表你们应该可以理解经过方法能够执行怎样的操做。

除此之外,HTTP消息中还有一些用来表示附加信息的头字段。客户端向Web服务器发送数据时,会先发送头部字段,而后再发送数据。

收到请求消息以后,Web服务器会对其中的内容进行解析,根据这些要求来完成本身的工做,而后将结果存放在响应消息中。

在响应消息的开头有一个 状态码解释信息,它用来表示操做的执行结果。

后面就是头字段和网页数据。响应消息会被发送回客户端,当收到以后,浏览器会从消息中读出所需的数据并显示渲染。到这里,HTTP的整个工做就以完成。

其中最经常使用的一个是GET方法,通常当咱们访问Web服务器获取网页数据时,使用的就是GET方法。

另一个就是POST方法,咱们在表单中填写数据并将其发送给Web服务器时就会使用这个POST方法,当咱们在网上商城填写收货地址和姓名,或者是在网上填写问卷时,都会遇到带有输入框的网页,而这些能够输入信息的部分就是表单。

使用POST方法时,URI会指向Web服务器中运行的一个应用程序的文件名,典型的例子包括“index.cgi”“index.php”等。而后,在请求消息中,除了方法和URI以外,还要加上传递给应用程序和脚本的数据,也就是表单中填入的数据。当服务器收到消息后,Web服务器会将请求消息中的数据发送给URI指定的应用程序。最后,Web服务器从应用程序接收输出的结果,会将它存放到响应消息中并返回给客户端。

以后会有对HTTP进行详细整理,在此不作赘述。

生成HTTP请求消息

理解了HTTP的基本知识以后,咱们回到对浏览器自己的探索中。对URL进行解析以后,浏览器肯定了Web服务器和文件名,接下来就是根据这些信息来生成HTTP请求消息。

首先,请求消息的第一行称为 请求行

这里的重点是开头的方法,能够告诉Web服务器它应该进行怎样的操做。第一行的末尾须要写上HTTP的版本号,这是为了表示该消息是基于哪一个版本的HTTP 规格编写的。

第二行开始为消息头。里面存放了除了请求行外的额外详细信息。如日期、客户端支持的数据类型、语言、压缩格式、客户端和服务器的软件名称和版本、数据有效期和最后更新时间等。

写完消息头以后,还须要添加一个彻底没有内容的空行,而后写上须要发送的数据。这部分称之为消息体。消息体结束以后,整个消息也就随之结束。

发送请求后会收到响应

当咱们将上述请求消息发送出去以后,Web服务器会返回响应消息,响应消息的格式以及基本思路和请求消息是相同的,差异只在第一行上。

在响应消息中,第一行的内容为状态码和响应短语,用来表示请求的执行结果是成功仍是出错。状态码和响应短语表示的内容一致,但它们的用途不一样。状态码是一个数字,它主要用来向程序告知执行的结果,响应短语则是一段文字,用来向人们告知执行的结果。

向DNS服务器查询Web服务器的IP地址

IP地址基础

生成HTTP消息以后,接下来咱们须要委托操做系统将消息发送给Web服务器。首先咱们要查询网址中服务器域名对应的IP地址。(通讯必需要提供IP地址),简单回顾一下IP地址。

互联网和公司内部的局域网都是基于TCP/IP的思路来设计的,因此咱们先来了解 TCP/IP的基本思路。

TCP/IP的结构如图所示,就是由一些小的子网,经过路由器链接成大的网络。这些子网能够当作使用集线器链接起来的几台计算机,咱们将他们当作一个单位,称之为 子网。经过路由器链接起来造成一个 网络

在网络中,全部的设备都会被分配一个地址。这个地址就至关于现实中某条路上 的“××号××室”。其中“号”对应的号码是分配给整个子网的,而“室”对应的号码是分配给子网中的计算机的,这就是网络中的地址。“号”对应的号码称为网络号,“室”对应的号码称为主机号,这个地址的总体称为IP地址

经过IP地址咱们能够判断出访问对象服务器的位置,从而将消息发送到服务器。 前面这些就是TCP/IP中IP地址的基本思路。了解以后让咱们再来看一下实际的IP地址。

实际的IP地址是一串32比特的数字,按照8比特(1字节)为一组分红4组,分别用十进制表示而后再用圆点隔开。

这就是咱们日常常常见到的IP地址格式,但仅凭这些咱们没法区分哪部分是网络号,哪部分是主机号。在IP地址的规则中,网络号和主机号连起来总共是32比特,但这两部分 的具体结构是不固定的。

在组建网络时,用户能够自行决定它们之间的分配关系, 所以,咱们还须要另外的附加信息来表示IP地址的内部结构。 这一附加信息称为子网掩码

子网掩码的格式如图所示,是一串与IP地址长度相同的32比特数字,其左边一半都是1,右边一半都是0。其中,子网掩码为1的部分表示网络号,子网掩码为0的部分表示主机号。将子网掩码按照和IP地址同样的方式以每8比特为单位用圆点分组后写在IP地址的右侧,这就是上图(b)的方法。

域名和IP地址并用的理由

TCP/IP网络是经过IP地址来肯定通讯对象的,不知道IP地址就没法将消息发送给对方,因此在委托操做系统发送消息时,必需要先查询好对方的IP地址。

既然如此,那么在网址中不写服务器的名字,直接写IP地址不就行了吗?实际上,若是用IP地址来代替服务器名称也是可以正常工做的。然而,就像你很难记住电话号码同样,要记住一串由数字组成的IP地址也很是困难。所以,相比IP地址来讲,网址中仍是使用服务器名称比较好记忆。

既然如此,那干脆不要用IP地址而是用名称来肯定通讯对象不就行了吗?

从运行效率上来看,这并不能算是一个好主意。互联网中存在无数的路由器, 它们之间相互配合,根据IP地址来判断应该把数据传送到什么地方。,使用IP地 址只须要处理4字节的数字,而域名则须要处理几十个到255个字节的字符,这增长 了路由器的负担,传送数据也会花费更长的时间。同时路由器的速度是有极限的,而互联网内部流动的数据量已然让路由器疲于应付了,所以咱们不该该再采用效率更低的设计。

因而,如今咱们使用的方案是让人来使用名称,让路由器来使用IP地址。为了填补二者之间的障碍,DNS服务孕育而生。

Socket库提供查询IP地址的功能

向DNS服务器发出查询,并接收DNS服务器返回的响应消息。对于DNS服务器,咱们的计算机上必定有相应的DNS客户端,而至关于DNS客户端的部分称为DNS解析器,经过DNS查询IP地址的操做称为域名解析。

解析器其实是一段程序,它包含在操做系统的Socket库中,在介绍解析器以前,咱们先来简单了解一下Socket库。

Socket库其中包含的程序组件可让其余的应用程序调用操做系统的网络功能,而解析器就是这 个库中的其中一种程序组件。解析器的用法很是简单,Socket库中的程序都是标准组件,只要从应用程序中进行调用就能够了。

调用解析器后,解析器会向DNS服务器发送查询消息,而后DNS服务器会返回响应消息。响应消息中包含查询到的IP地址,解析器会取出IP地址,并将其写入浏览器指定的内存地址中。只要运行图中的这一行程序,就能够完成IP地址的查询。接下来,浏览器在向Web服务器发送消息时,只 要从该内存地址取出IP地址,并将它与HTTP请求消息一块儿交给操做系统就能够了。

DNS解释器内部原理

浏览器调用解析器时,程序的控制流程就会转移到解析器的内部。

经过让多个程序按顺序执行操做,数据就被发送出去了。顺带一提,向DNS服务器发送消息时,咱们固然也须要知道DNS服务器的IP地址。只不过这个IP地址是做为TCP/IP的一个设置项目事先设置好的,不须要再去查询了。 不一样的操做系统中TCP/IP的设置方法也有差别,解析器会根据设置的DNS服务器IP地址来发送消息。

解析过程

上述图片是查找www.google.com的IP地址过程。

首先在本地域名服务器中查询IP地址,若是没有找到的状况下,本地域名服务器会向根域名服务器发送一个请求,若是根域名服务器也不存在该域名时,本地域名会向com顶级域名服务器发送一个请求,依次类推下去。直到最后本地域名服务器获得google的IP地址并把它缓存到本地,供下次查询使用。

从上述过程当中,能够看出网址的解析是一个从右向左的过程: com -> google.com -> www.google.com。

可是你是否发现少了点什么,根域名服务器的解析过程呢?事实上,真正的网址是www.google.com.,并非我多打了一个.,这个.对应的就是根域名服务器,默认状况下全部的网址的最后一位都是.,既然是默认状况下,为了方便用户,一般都会省略,浏览器在请求DNS的时候会自动加上,全部网址真正的解析过程为: . -> .com -> google.com. -> www.google.com.。

经过缓存加快DNS服务器的响应

有时候并不须要从最上级的根域开始查找,由于DNS服务器有一个缓存功能, 能够记住以前查询过的域名。若是要查询的域名和相关信息已经在缓存中,那么就 能够直接返回响应,接下来的查询能够从缓存的位置开始向下进行。相比每次都从 根域找起来讲,缓存能够减小查询所需的时间。

而且,当要查询的域名不存在时,“不存在”这一响应结果也会被缓存。这样,当 下次查询这个不存在的域名时,也能够快速响应。

这个缓存机制中有一点须要注意,那就是信息被缓存后,本来的注册信息可能会发 生改变,这时缓存中的信息就有多是不正确的。所以,DNS服务器中保存的信息都 设置有一个有效期,当缓存中的信息超过有效期后,数据就会从缓存中删除。而 且,在对查询进行响应时,DNS服务器也会告知客户端这一响应的结果是来自缓存中 仍是来自负责管理该域名的DNS服务器。

委托协议栈发送消息

知道了IP地址以后,就能够委托操做系统内部的协议栈向这个目标IP地址发送消息。

向操做系统内部的协议栈发出委托时,须要按照指定的顺序来调用Socket库中的程 序组件。首先咱们解释一下什么是协议栈以及套接字。

协议栈

操做系统中的网络控制软件称之为协议栈,表面上是看不出来的,比较抽象。

分为4个部分,之间的上下关系是遵循必定规则。

最上层是网络应用层程序,其中包含浏览器、电子邮件客户端、Web服务器等,接下来是Socket库,其中包含DNS解析器

接下来就是操做系统内部TCP、UDP他们接受应用程序委托执行收发数据的操做。IP协议负责控制网络包收发操做,将网络包分割成一个一个网络包,并进行传输。在IP协议中存在着ARP协议和ICMP协议,ARP协议用于根据IP地址查询相应的以太网MAC地址

IP层下面就是网卡驱动程序,负责控制网卡硬件,最下面的网卡则负责实际的收发操做。

套接字

在协议栈内部,有一块内存空间存放着控制信息的内存空间,这里记录了控制信息,一般包含通讯双方的IP地址、端口号、通讯状态等。协议栈须要经过这些控制信息判断下一步的行动,这些控制信息一般称之为套接字。

了解协议栈和套接字以后,咱们来看一下调用Socket库的具体过程。

建立套接字

经过调用socket函数,首先分配一个套接字所须要的内存空间,而后写入初始化状态。

链接服务器

初始化后,浏览器会调用connect函数,随后的协议会将本地的套接字与服务器的套接字进行链接。

具体细节(TCP三次握手)

经过调用connect函数,经过TCP头部中的发送方和接收方端口号能够找到要链接的套接字,将头部的控制位SYN比特设置为1,表示能够链接。同时设置适当的序号和窗口大小。

找到服务器的套接字后,套接字中会写入相应的控制信息,将状态更改成正在链接

服务器开始返回响应,在TCP头部中写入对方的IP地址、端口号,以及SYN比特。同时将ACK控制位设置为1.表示已经接收到相应的网络包。(在网络传输过程当中,常常会发生错误网络包所以丢失。所以双方在通讯时必须相互确认网络包是否已经送达而设置ACK比特就是用来进行这一确认的)

网络包返回到客户端后,经过IP模块到达TCP模块,并经过TCP头部的信息确认链接服务器的操做是否成功。当确认成功后,会向套接字中写入服务器的IP地址、端口号信息,将状态更改成链接完毕。同时响应服务器,将控制位的ACK比特设置为1并发回服务器。

当服务器收到这个返回包后,链接操做完成,如今的套接字随时能够进入收发数据的状态。

收发数据

将HTTP请求消息交给协议栈

经过调用write函数,把要发送的数据交给协议栈。协议栈收到数据后并非立刻发送出去,而是将数据存放在内部的发送缓存区中,并等待应用程序的下一段数据。

这样作的目的是:

​ 优化网络效率。若是收到数据就发送,就可能致使发送大量的小包数据。

关于累计多少数据才能发送跟下面几个要素来进行判断:

第一个要素是判断每一个网络包能容纳的数据长度。经过MTU参数(网络包中最大长度,一般是1500字节)减去头部(TCP头部 IP头部)的长度,获得网络包中可以容纳的最大数据长度,称之为MSS。当发送缓冲区的数据长度超过或接近MSS时再发送数据,这样能够避免发送大量小包数据问题。
复制代码

第二个要素是时间。若是应用程序发送数据频率不高,每次都会等到缓冲长度接近MSS时才发送数据,会致使发送延迟。为此协议栈内部有一个计时器,当通过必定时间后,就会把网络包发送。
复制代码

若是仅靠协议栈来判断发送的时机可能会致使一些问题,所以协议栈给应用程序保留了控制发送时机的能力。例如:

浏览器这种会话型应用程序在向服务器发送数据时,一般是使用直接发送的选项。好比指定“不等待填满缓冲区直接发送”。从提升浏览器的响应速度,下降延迟。

拆分较大数据

一般HTTP请求消息不会太长,一个网络包就能装下。但若是是要提交表单数据,长度可能会超过一个网络包所可以容纳的数据量。例如博客、论坛、评论等场景。

这种状况下,发送缓冲区数据长度就会超过MSS,因此数据会以MSS为单位进行拆分,将拆分出来的每块数据放进单独的网络包中。

当要发送这些网络包时,会在数据包前面打上TCP头部,而且根据套接字中记录的控制信息,标记IP地址和端口号后转交给IP模块进行发送操做。

使用ACK号确认网络包已收到

由于TCP协议是提供可靠的字节流服务,因此具有确认对方是否接收到网络包,以及当对方没有收到时进行重发功能,所以在发送网络包后,还须要进行确认操做。

原理

TCP协议在进行拆分数据时,会计算好每一块数据至关于从头开始的第几个字节,将计算好的字节数写在TCP头部中,这正是“序号”字段的做用。发送数据的长度是根据网络包的长度减去头部长度就能够计算出来,因此不写入TCP头部中。

根据序号和数据长度就能够准确知道数据是从第几个字节开始,长度为多少。而且接收方还能够能确认收到的网络包是否有遗漏。

若是没有遗漏,接收方会计算如今一共收到多少字节,而后将这个数值写入TCP头部中的ACK号,回传给发送方。返回的ACK号的操做称为确认响应。经过这样的方式,发送方就可以确认对方到底收到了多少数据。

“序号”是经过随机计算出一个初始值,避免数据被攻击。所以在创建通讯的时候(三次握手时)须要将开始收发数据以前的随机初始值通知通讯对象。在将SYN设置为1时,还须要同时设置序号字段的随机初始值。SYN(synchronize)的本质是经过告诉初始序号使得通讯双发保持步调一致,以便完成后续的数据收发检查。

TCP采用这种补救措施来确保对方是否接收到数据,在获得对方确认之间,全部的数据将存放在发送缓冲区中,当对方没有返回某个包对应的ACK号,就会从新发送这些包。

但若是发生网络中断,服务器宕机等问题,不管TCP怎样重发数据确定是徒劳的。所以在TCP尝试几回无效重传后会强制结束通讯,并向应用程序报错。

TCP补救机制以及优化策略

根据网络包平均返回时间调整ACK号超时时间

当网络传输繁忙时就会发生拥塞,ACK号的返回会变慢。这时咱们就要将等待的时间设置的稍微长一点,避免发生重传数据包以后前面的ACK号才姗姗来迟。

由于真实环境不可预测,因此将等待时间设置为一个固定的值并非一件好办法,所以TCP采用了动态调整等待时间的策略。简单来讲就是TCP会检测ACK号的返回时间,当ACK号返回的变慢,则会延迟超时时间。当ACK返回变快时,相应的会缩短超时时间。

使用窗口有效管理ACK号

每发送一个包就等待一个ACK号的方式是最简单最容易里的方式。但等待ACK号这段时间,若是不作其余事情,实在是太浪费效率了。所以TCP采用滑动窗口方式来管理数据发送和ACK号的操做。 简单来讲就是发送一个数据包后,不等待ACK号返回,而是直接发送下一个数据包,这样就有效利用起来等待ACK号这段时间。

虽然这样作可以减小等待ACK号所浪费的时间,可是若是不等待返回ACK号就连续发送包,会出现发送包频率超过接收方处理能力的状况。

为了不接收缓冲区的数据溢出,能够经过接收方告诉发送方我可以接受多少数据(TCP头部中的窗口字段可以将本身可以接受的数据量告诉对方),而后发送方根据这个值对数据发送操做进行控制,这就是滑动窗口的基本思路。

ACK与窗口的合并

要提升收发数据的效率,还须要考虑返回ACK号和更新窗口的时机。首先分别分析一下更新窗口和返回ACK号的时机。

  • 更新窗口的时机:

​ 当接收到的数据填入缓冲区时,不必向发送方更新窗口大小。由于发送方在每次发送数据时,减掉已经发送的数据长度就能够自行计算出当前窗口的剩余长度。所以更新窗口大小的时机是接收方从缓冲区取出数据传递给应用程序的时候,这个操做的时机是发送方没法得知的。

​ 所以当接收方将数据传输给应用程序时,致使缓冲区容量增长,就须要告知发送方。

  • 更新ACK的时机:

​ 当接收方收到数据时,若是确认没有问题,就应该向发送方返回ACK号,所以咱们认为应该收到数据以后就立刻进行这一操做

结合这两个因素来分析,当发送方的数据到达后,马上返回一个ACK号,当数据传递给应用程序后,更新窗口大小。若此以来,每收到一个包就须要分别发送ACK号和窗口更新的两个单独数据包,会致使网络效率降低。

所以,接收方在发送ACK号和更新窗口时,并不会立刻把包发送出去,而是等待一段时间,将两个通知合并成一个包进行回传。

举例来讲:

​ 当等待发送ACK号时,正好须要更新窗口,这时就能够将数据合并成一个包,减小包的数量。

​ 当须要连续发送多个ACK号时,也可减小包的数量。由于ACK号表示的是已经接受到的数据量,所以只发送最后一个ACK号就能够,中间的能够所有省略。

​ 同理当须要发送多个窗口更新时也能够减小包的数量,由于窗口大小表示缓冲区剩余空间,所以只发送最后一个窗口大小,省略中间过程。

接收HTTP响应消息

具体操做在发送消息的时候已经说明,在这里简单总结一下接受HTTP响应的过程。

​ 浏览器委托协议栈发送请求消息以后,会调用read函数来获取响应消息。控制流程会经过read函数转移到协议栈,协议栈执行接下里的操做。

​ 首先协议栈会检查数据块和TCP头部信息,判断数据是否有丢失,若是没有问题返回ACK号。同时协议栈将数据暂存到接收缓冲区中,并将数据按顺序链接起来还原出原始的数据,最后将数据交给应用程序。

从服务器断开(TCP四次挥手)

收发数据结束的时间点应该是应用程序判断全部数据已经发送完毕,这时候数据发送完毕的一放会发起断开过程,但不一样的应用程序也会选择不一样的断开时机。

​ 假设以服务器一方发起断开过程

​ 服务器会调用Socket库中的close函数,生成包含断开信息的TCP头部,将控制位中的FIN比特设置为1.同时套接中会记录断开操做的相关信息。

​ 当收到服务器发来的FIN为1的TCP头部时,客户端的协议栈也会将本身的套接字标记为进入断开操做状态。为了告知服务器已收到数据包,客户端回传一个ACK号。

​ 只要接受服务器返回的全部数据,客户端的操做也就随之结束了。所以客户端应用程序会调用close函数来结束收发操做。同服务器端同样生成FIN比特为1的TCP包委托IP模块发送给服务器。

​ 服务器收到后会返回一个ACK号,到如今,整个客户端和服务器的通讯所有结束。

当浏览器拿到响应到的数据后,会... 浏览器是如何渲染出页面的?

彩蛋...

感谢各位大佬听我叨叨完,文章是参考《网络是怎样链接》,固然各位大佬可能有些早已阅读过,或者啃过经典大教材等等..快圣诞节了,也不知道送你们点什么好..把我收藏的电子书送给你们吧(目前在看的)..还有各类学习资源。

虽然纸质书看起来舒服,可是电子书胜在便携还有作笔记时候你懂的!

若是有须要的请私信我...记得点赞哈!资源就不截图了,省得...侵权...

至此,从输入url到浏览器的渲染就整理结束了,下一篇文仍是把学过的HTTP整理整理..

避免狗熊掰棒子。

下回见,再见各位大佬!