当把网址输入到浏览器地址栏,按下回车键后,一段浩瀚的互联网历险就开始了。
本文将结合个人所学并参考《网络是怎样连接的》一书,简要介绍从输入网址到网页最终呈现这个过程中涉及的诸多软硬件的原理及工作方式。
目录
零、URL网址
一、应用层(浏览器、HTTP协议)
(一)Web浏览器的初步工作
(二)调用操作系统Socket库进行DNS查询
(三)发送HTTP请求
二、传输层(TCP、UDP)
(四)程序对程序的连接
三、网络层(IP)
(五)确立主机对主机的连接
四、链接层(网卡、以太网)
(六)在以太网上传输
五、实体层(路由器、调制解调器、网关等)
(七)光信号/电信号经各种设备传输
(八)服务器响应请求
参考资料
网址通常是一个URL
,即“统一资源定位符”
,是万维网的创造者——蒂姆·伯纳斯·李的发明之一。
就像现实生活中的地址一样,它的用处是规范地在互联网上标识资源的地址。
统一资源定位符URL的标准格式如下:
协议类型:[//服务器地址[:端口号]][/资源层级UNIX文件路径]文件名[?查询][#片段ID]例如:https://github.com/JuniorTour...
其中的协议类型://
,端口号
,文件名
均可在某些情况下省略。
例如:
协议类型://
。而服务器地址
出于便于人类记忆的目的,通常会是一个具有语义化的域名
,而不是更加精确的标识一台计算机在网络世界中位置的IP地址
或MAC地址
。
了解完了URL,就该主角们纷纷亮相了。
千里之行始于足下。
首先,浏览器会解析出所输入的URL之中包含的协议,服务器地址,路径,端口号等信息,并根据这些信息决定向谁请求什么资源。
但是因为URL中的服务器地址通常是一串具有一定语义的英文字符:域名
,这个地址不够精确,它可能代表不止一台服务器,更无法指明服务器的具体位置,浏览器或者说计算机并不能依靠这个地址在浩瀚的互联网世界中精确地找到目标服务器。
所以还需要进一步借助DNS查询
得到IP地址
才能定位目标服务器的位置。
在进行DNS查询获取IP地址之前,浏览器会先在内部的DNS缓存中查找是否存在所查询域名的IP地址。
Chrome浏览器可以通过访问 chrome://net-internals/#dns
查看浏览器的DNS缓存情况
如果没有通过浏览器的DNS缓存查询到IP,还会进一步借助操作系统查询本地hosts文件看是否有可用的DNS缓存记录
Windows 系统通常位于C:WindowsSystem32driversetc 目录下
如果都没有找到目标域名的地址,就要向DNS服务器请求查询了。
浏览器并不能直接找到DNS服务器进行查询,需要操作系统中Socket
库的帮助。
Socket
库是操作系统提供的一组调用操作系统网络功能的程序组件集合,在计算机网络之中有非常重要的地位,后续我们还会介绍和它相关的内容。
DNS
是Domain Name System
即域名系统
的缩写。这个系统的主要功能就是根据域名
获取IP
地址。
DNS服务器
是域名系统
的具体组成部分,其数量有很多很多,分布在现实世界中的不同位置和网络世界中的不同层次,用于响应不同地区、不同层次域名的查询请求。
DNS服务器的地址需要记录在本机之中,Windows系统中可以在控制面板\网络和 Internet \网络连接
中右键进入任意一个连接的属性,双击Internet协议版本4(TCP/IPV4)
查看。
在这个面板中可以看到设置如下四项的输入框:
值得一提的是,因为这四项内容比较复杂,不便于普通用户设置,所以个人电脑中这几项通常都会被设置为自动获得
,使用动态分配IP
。
这样,当计算机连入网络时,就会利用DHCP协议
向负责管理本区域网络所有IP地址的DHCP服务器动态地请求一个IP地址。
调用Socket
库发送DNS查询请求时,会向本机中记录的DNS服务器IP地址发送请求,接收到DNS查询请求的DNS服务器会优先在自己缓存的路由表
中查询目标域名对应的服务器IP地址。
如果在缓存之中没有找到,DNS服务器则会根据域名的层次分级逐层查询。
例如,查询 https://github.com/JuniorTour 这个域名对应的 IP 时,DNS服务器会逐层向根域名 (.root) 服务器、顶级域名 ( .com ) 服务器、次级域名 ( .github ) 服务器发送查询请求,自顶向下,直到找到域名对应的IP地址。
顺带一提,次级域名 ( .github ) 之下,还有一层可以由用户自己分配的三级域名。github.com
这个域名实际上是省略了www
这个三级域名的写法。
知道了URL对应的IP地址后,就可以向这个地址发送HTTP请求,请求所需的网络资源了。
浏览器浏览网页用的是属于应用层的HTTP协议
,如果想要获取一个网页,就需要向HTTP服务器发送HTTP请求
,在响应的数据中获取网页数据。
通常来说,一个HTTP请求大概长这样子:
GET /JuniorTour HTTP/1.1 Host: github.com User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36 Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9 Accept-Encoding: gzip, deflate Accept-Language: zh-CN,zh;q=0.9,en;q=0.8 Cookie: authorstyle=no
它主要由以下几部分组成:
<request-line> 请求行 <headers> 请求头 <blank line> 空格 <request-body> 请求数据
浏览器在掌握了HTTP服务器的IP地址以及文件名、Cookie、用户代理(User-Agent)等发送请求所需的信息后,就会根据这些信息生成相应的HTTP请求,转交给协议栈
,由它来具体执行发送请求。
协议栈是操作系统中的网络控制软件,主要由TCP模块
、UDP模块
和IP模块
组成,分别负责用不同的协议收发数据。
在HTTP请求转交给协议栈
发送之前,协议栈或者说是客户端主机首先需要连接上服务器。
这个建立连接的过程就是著名的三次握手
,遵循的是TCP协议
。
其大致过程如下:
第一次握手:建立连接请求,客户端发送请求报文,将TCP头部的SYN字段标为1,表示连接。第二次握手:连接建立中,服务器收到客户端的建立连接请求报文后,会发送响应报文,也会将TCP头部的SYN字段和ACK字段标为1。
第三次握手:连接建立,客户端收到服务器的确认连接响应后,会再次向服务器发送一个ACK字段为1的TCP报文,发送完这个报文后,客户端和服务器都会进入“连接已建立”状态,随时准备收发报文。
打一个形象的比喻就是这样的:
客户端告诉服务器:咱们要开始连接了哦~服务器听到后,回复客户端说:我知道了!
客户端和服务器都确认对方收到消息后就会知道:行了,连接建立了~~
收发数据结束后,还需要断开连接,也就是四次挥手
。
以Web为例,服务器会在向浏览器返回响应信息后,由服务器一方发起断开连接。其大致过程如下:
第一次挥手:服务器发送断开连接请求,服务器完成数据响应后,会通过服务器的协议栈向客户端发送一个FIN字段为1的TCP报文。第二次挥手:客户端确认断开请求,客户端的协议栈接收到服务器的断开连接请求报文后,会向服务器返回一个ACK字段为1的响应报文,表示已收到断开连接请求。
这时客户端的应用程序就会来读取此次响应的数据,客户端的应用程序得到了数据后,也会再向服务器执行一次断开连接操作。
第三次挥手:客户端发送断开连接请求,客户端向服务器发送一个FIN字段为1的TCP报文。
第四次挥手:服务器确认断开连接,收到客户端发来的断开连接请求后,服务器会相应地返回一个ACK字段为1的响应报文,表示已收到断开连接请求。
还是用一个更形象的比喻来描述一次:
服务器发送完数据后对客户端说:数据发完了哦!客户端得知后回复说:行,我知道了。
客户端检查、储存数据后,又对服务器说:确实都发完了,咱们断开连接吧!
服务器听到后说:好的,我知道了,断开连接吧。
从上述过程也可以看出:TCP协议
是一个非常重视可靠性
的协议,连接的建立和断开都经历了再三的重复和确认,有效地保证了网络数据传输的可靠性
。
发送HTTP请求到一个服务器来获取响应,还有一个问题没有解决。
那就是服务器主机收到HTTP请求后如何判断这个请求是发给这台服务器主机上哪一个程序的,一台服务器主机上可能会运行着多个服务器或程序,该如何判断这个请求是发给哪一个服务器的呢?
同理,当响应传回客户端的个人计算机,又该如何判断这个响应是发给哪一个应用程序,是Chrome,还是微信呢?
这就需要另一个指标来标识一台计算机上运行的不同程序。
这个指标就是端口。
端口(port)
,其实是每一个使用网卡
的程序的编号。
不同的程序占用不同的端口,每个数据包都指定发送到主机的特定端口,不同的程序就能依据端口号收到自己所需要的数据。
在两台主机建立连接之后,建立端口对端口的通信,依据端口号确定数据到底是发送给这台电脑中的哪一个程序,这就是传输层
的主要作用。
为了将HTTP请求发送给服务器主机的指定程序,需要委托协议栈中的TCP模块对HTTP请求的数据包进行封装,加上带有端口信息的TCP头部。
这里的端口号也来自一开始在浏览器中输入的URL,通常URL中的端口号可以省略不写,此时就会把事先约定好的HTTP默认端口号: 80或HTTPS默认端口号: 443, 当作目标端口号。
如果指定了端口号,例如:https://github.com:443/JuniorTour ,则会相应地根据指定的端口号附加在HTTP请求上,生成TCP数据包
。
大多数应用程序都使用TCP协议来收发数据的,但是有些程序也会使用更简单、更高效的UDP协议
。
例如向DNS服务器查询IP用的就是UDP协议。
UDP协议和TCP协议的关键不同点在于,UDP协议为了追求高效率,并不会在收发数据的前后进行确认,也就是前面说的三握四挥
,这样的实现相比TCP协议收发数据会更高效。
但是相应的,收发数据的可靠性就会有一定打折,传输过程中发生丢失部分数据包的可能性会更大,即所谓的丢包
。
也正是因为效率更高、可靠性较差这两个特点,让UDP协议在短数据收发、多媒体数据收发上有独特的应用。
前面的工作都完成后,就要在两台主机之间建立连接了。
为了实现这一点需要用到明确标识两台主机的标志,就是之前曾多次提及的IP地址
。
IP地址的规范主要有两个版本,即 IPv4 和 IPv6 ,他们分别规定了不同长度的IP地址。
以 IPv4 为例,IP地址由32个二进制位组成,例如:11000000.10100010.00000001.00000001,一般习惯用十进制表示,例如:192.168.1.1。
IP地址的作用除了标识一台计算机在网络世界中的地址外,还有判断两台计算机是否属于同一个子网络
的功能,这项功能的实现需要子网掩码
的辅助。
子网掩码也是一段32位的二进制数,例如:11111111.11111111.11111111.00000000,用十进制表示即为:255.255.255.0,其代表网络部分的全为1,代表主机部分的全为0。
子网掩码的用法是分别和两个IP地址做与运算
,如果得出的结果相同,则可以判定两个IP属于同一个子网络。
这两项内容的设置和查看方式已经在前文的DNS一节有所介绍。
为了在两台主机之间建立连接,就需要在数据包中添加带有IP地址的IP头部
,并进一步向外传输。
IP模块还会做一件非常重要的事情,就是通过ARP(Address Resolution Protocol,地址解析协议)
,以广播的形式向同一以太网内的所有设备提问:“ XXX.XXX 这个 IP 地址是谁的?请把你的MAC地址告诉我”,之后就会有同一子网内的其他设备(通常是路由器)回复IP 地址
对应的MAC地址
。
另外ARP也是有缓存的,会缓存查询结果。
获取到MAC地址之后,IP模块就会再给数据包加上一个带有MAC地址信息的MAC头部
并转交给网卡传输。
IP模块的工作完成后,生成的网络包还是不能直接发送给对方,因为此时的网络包还只是内存中的一串数字信号
,要在网线上传输,还需要把这些数字转换为电信号或光信号。这个工作是由网卡及其驱动程序完成的。
网卡
是计算机上常见的硬件之一,主要功能是信号和数据的编解码、数据包的收发等。
网卡内部有复杂的各种构造,网卡作为一个硬件,和操作它的软件——网卡驱动之间也有多种多样的交互,但在此为了简化这一过程,我们只了解最关键的一小部分细节。
网卡的ROM(只读存储器)中保存着全世界唯一的MAC地址
,这个地址是生产网卡时写入的。
以太网上数据包的发送和接收地址都是MAC地址
,网卡会将之前IP模块生成的数据包按照一定的规则转换为可在现实世界网线中传输的信号,通过网线发送出去。
至此,客户端的工作已经完成,数据包进入到了以太网之中进一步传输。
在这一章的开头,我想先介绍四个容易混淆的概念:互联网、因特网、以太网和万维网。
Internet
,互联网是意译,因特网是音译,两者在今天都是泛指全球范围内由海量网络相互连接而成的庞大网络
。早先,大写开头的Internet
专指IP协议架设而成的网络,小写开头的internet
则泛指其他各种网络,小写代表的意义包含大写。而今天,在各种网络中,以IP协议架设而成的网络已经占据统治地位,是现代人类生活的一部分。小写开头的internet
的泛指含义已经没有意义,所以,今天这两个词指的是同一个事物,即全球范围内由海量网络相互连接而成的庞大网络
。World Wide Web
简称Web
,是架构在互联网之上的一项服务
,由英国科学家蒂姆·伯纳斯-李于1989年发明,这项服务的核心由统一资源标识符(URI)、超文本传送协议(HTTP)、超文本标记语言(HTML)构成,并借助浏览器等工具为其用户提供各种信息和资源。Ethernet
是一项局域网通信技术
,它所涵盖的内容十分丰富,其技术标准是著名的IEEE 802.3标准
,某种程度上 IEEE 802.3 就是以太网,其规定了网络连接的规范和电子信号的分组模式等诸多内容。我们要了解的重点是以太网,以太网具体的数据传输方式是:广播
,即向整个网络上的每一台设备都发送信号,就像是在一间屋子里喊话一样,一个人喊的话,同一间屋子的所有人都能听到。但是为了明确指出这句话是说给谁的,还需要加上存储在网卡之中标识网络内的每一个节点的MAC地址,就像喊话的时候带上了人名一样。
以太网中以广播的形式向一个MAC地址传输数据的方式有很大的弊端,互联网是由许许多多子网络构成的庞大网络,当两台设备不在同一个子网络之中,却仍采用广播形式时,不仅会有效率上的巨大浪费,也不能准确地传输到目的地。
针对这个弊端,在以太网中传输的数据会依据IP地址和子网掩码判断是否属于同一个子网络。
如果和目标主机不在同一个子网络之中的,会像某一个路由
发送数据,由它代理转发到其他子网络之中。相反,当属于同一个子网络时,则仍然采用广播的方式传输数据。
数据被处理成光信号/电信号后,会经过网线、路由器、调制解调器、网关等各种设备的处理转发。
这一部分已经进入了网络硬件的领域,为了保持文章篇幅,不做过多描述。
经历了万水千山、长途跋涉,最初的请求终于到达了目标服务器主机。
服务器主机得到数据包后,会经过网卡、网卡驱动程序、服务器协议栈的层层解析,最终将请求交给服务器处理。服务器处理后,会生成相应的HTTP响应
,再将数据包返回给客户端,这个过程几乎就是上文所述的逆向操作。
客户端的浏览器得到了服务器返回的HTTP响应
后,将其中的内容解析出来,渲染、呈现在了我们的眼前。
回过头来再看,这几章对应的分别就是互联网的五层模型:
《网络是怎样连接的》-【日】户根勤 著, 周自恒 译
互联网协议入门-阮一峰
what-happens-when-zh_CN
[简析TCP协议中的三次握手和四次挥手](https://segmentfault.com/a/11...
有任何想法、建议,欢迎在评论区留言或来GitHub 和我交流:https://github.com/JuniorTour/ ,我会尽快回复的