有个经典的面试题:从在浏览器中输入URL到页面返回发生了什么,这个问题其实有很多回答的角度,可大可小,本书是从网络角度用一本书的内容刚好回答了这个问题。本书内容详实,插图丰富,这里的笔记,只是记录我认为重要或者我所不太了解的知识,并且也只是尽量能用文字描述出来的内容,我没精力也没必要摘抄出一个电子版。查看目录 豆瓣读书。
1.1 生成 HTTP 请求消息
浏览器中输入的地址准确说是 URL,URL 开头的文字表示浏览器应当使用的方法(协议),常见的如 :
- http:访问 Web 服务器
- ftp:访问 FTP 服务器
- file:读取客户端计算机本地文件时
- mailto:发送电子邮件
典型的URL格式如:http: + // + Web服务器名 + / + 目录名 + / ... + 文件名
,访问 Web 服务器时,浏览器对 URL 进行解析后生成相应的 HTTP 请求消息。
HTTP 请求消息的格式:
<方法><空格><URI><空格><HTTP版本> # 请求行,通过这一行大致了解请求内容
<字段名>:<字段值> # 这几行称为消息头,每行包含一个头字段,用于表示请求的附加信息。消息头的行数依具体情况可变,一直延伸到空行为止
...
...
...
<空行>
<消息体> # 包含客户端向服务器发送的数据,例如用POST方法向Web服务器发送的网页表单数据
HTTP 响应消息的格式:
<HTTP版本><空格><状态码><空格><响应短语> # 状态行,并包含用来解释状态码的短语
<字段名>:<字段值> #这几行称为消息头(同理)
...
...
...
<空行>
<消息体> # 消息体包含服务器向客户端发送的数据,例如从文件中读取的数据,或者应用程序输出的数据等。消息体的格式会通过消息头中的 Content-Type 字段来定义(MIME 类型参见6.4节)
具体案例直接打开浏览器 Network 抓包即可。
image1.2 向 DNS 服务器查询 Web 服务器的 IP 地址
虽然浏览器可以生成和解析 HTTP 消息,但本身并不具备网络发送功能,所以需要委托操作系统(OS)来实现,但是委托 OS 时,需要提供 IP 地址。
IP 相当于门牌号,xx号xx室
,号
对应网络号,室
对应主机号,网络号+主机号 = IP 地址
IPv4是32位(bit)地址,每8位一组(字节),分为4组并用点分十进制表示:10.11.12.13
,为了区分网络号和主机号之间的界限,所以还需要采用与IP地址相同格式表示的子网掩码:10.11.12.13/255.255.255.0
。
子网掩码是 IP 地址的附加信息,子网掩码还原为2进制:11111111.11111111.11111111.0
,其中为1的部分表示网络号,为0的部分表示主机号,所以 10.11.12.13
这个 IP 地址中,网络号是 10.11.12
,主机号是13
。
除了 10.11.12.13/255.255.255.0
这样表示,还可以采用网络号的位数 10.11.12.13/24
表示同样的子网掩码,数一数刚才有多少个1就知道为什么是24了。
现在,浏览器调用操作系统 Socket 网络库中的 解析器
(即 DNS 客户端),拿着用户输入的域名向预先设定的 DNS 服务器发送查询消息,以便查到 IP 地址,但其实 Socket 库中真正发送消息时的操作,还需要进一步委托操作系统内部的协议栈(协议驱动、TCP/IP驱动)来执行,即通过网卡发送消息到 DNS 服务器。
Socket 库是应用程序(Web浏览器)和操作系统内部协议栈之间的桥梁。为便于理解,Socket 库和协议栈可看作一个整体。
1.3 全世界 DNS 服务器的大接力
DNS 服务器会从域名与 IP 地址的对照表中查找相应的资源记录,并返回 IP 地址。
互联网中的服务器是不计其数的,所以也需要成千上万台 DNS 服务器,DNS 中的域名都是用点来分隔的,越靠右的位置层级越高,下级域称为子域。
-
下级域的 DNS 服务器的 IP 地址注册到上一级的 DNS 服务器中,以此类推,
tieba.baidu.com
这个域名到了com
域似乎就是最顶层了,其中保存着下级 DNS 服务器的信息,但事实上,如com
域,其上还有一级域,称为根域,它没有名字,书写时常被省略,非要明确表示根域,则tieba.baidu.com
最右边再加个点:tieba.baidu.com.
,这个点就代表根域。 -
根域的 DNS 服务器信息保存在互联网中所有的 DNS 服务器上,如此客户端只需就近找到一台 DNS 服务器(即客户端的 TCP/IP 设置中填写的 DNS 服务器地址)就能访问根域 DNS 服务器了。
-
客户端先和最近的 DNS 服务器通讯,然后和根域 DNS 服务器通讯,然后和
com
域 DNS 服务器通讯,然后和baidu.com
域 DNS 服务器通讯,最后和tieba.baidu.com
域 DNS 服务器通讯,找到了tieba.baidu.com
的 IP 地址。
但是刚才说的只是基本原理,真实互联网中未必真的如此。一台 DNS 服务器可以管理多个域的信息,所以可能会跳跃式的查询,另外 DNS 服务器会缓存(缓存设定了有效时间以免帮倒忙)历史查询记录(包括某域名不存在),所以不一定总要从根域开始查找。
1.4 委托协议栈发送消息
知道 IP 地址之后,就可以向操作系统内部的协议栈发出委托,需要按照指定顺序调用 Socket 库中的程序组件。收发数据的两台计算机需要建立通信管道,通信管道两端的数据出入口称为 套接字
。使用 TCP 协议收发数据大概分为以下 4 个阶段:
1. 一般服务器先创建套接字(创建套接字节段)
socket(<使用IPv4>, <流模式>, ...);
2. 将管道连接到服务器端的套接字(连接阶段)
connect(<描述符>, <服务器IP地址和端口号>, ...);
3. 收发数据(通信阶段)
write(<描述符>, <发送数据>, <发送数据长度>);
<接收数据长度> = read(<描述符>, <接收缓冲区>, ...);
4. 断开管道并删除套接字(断开阶段)
close(<描述符>);
调用 socket
后套接字创建完成后,协议栈返回一个描述符,当调用 connect
程序组件时,需要提供描述符、服务器 IP 地址和端口号。
描述符是应用程序用来识别计算机自身内部的套接字,而端口号是客户端和服务器之间用来识别对方套接字。服务器上所使用的端口号是事先约定的,比如 Web(HTTP) 是 80 号端口,电子邮件是 25 号端口。另外,客户端在创建套接字时,协议栈会为该套接字随便分配一个端口号,当连接时将此端口号通知给服务器。
HTTP 协议规定,Web 服务器发送完数据后一般应主动断开连接,当浏览器接收完数据后,也会进入断开阶段。
HTTP1.0 版本的工作方式是每次 TCP 连接只能发送一个请求,当服务器响应后就会关闭这次连接,下一个请求需要再次建立 TCP 连接,即不支持 keepalive。一个网页中有多张图片时,可能需要多次重复刚才的 4 个阶段。所以后来在 HTTP1.1 版本中 TCP 连接默认不关闭,一个TCP连接可以允许多个HTTP 请求。
(本章完)
网友评论