(一)浏览器生成消息

以前知道的是浏览器发出请求,服务器返回响应。实际上浏览器经过了解析网址、生成 HTTP 消息、查询服务器 ip 地址、委托协议栈(操作系统里的网络控制软件)发送消息。下面就来看看这些步骤具体是怎么完成的。

一、浏览器解析网址

所谓的解析网址就是把一个网址拆分开来,获取其中包含的信息。

网址就是 URL(Uniform Resource Locator 统一资源定位符)。除了我们常见的 http:// 开头的网址,还有 ftp:// 、file://、mailto:。以一个 http 开头的网址为例:

http://www.lab.glasscom/dir1/file1.html

  • http 表示访问数据源的机制,也就是协议
  • // 后面跟着的就是服务器的名称,即为 www.lab.glasscom
  • /dir1/file1.html 表示要访问的资源的具体位置,这里就是 dir1 目录下的 file1.html 文件

二、浏览器生成消息

解析 URL 之后就可以根据需要生成 HTTP 消息了

根据需要使用不同的方法,如果是想发送数据的服务器处理就使用 POST 方法,如果是想从服务器获取数据就用 GET 方法。


消息格式:

1
2
3
4
5
6
7
<方法><空格><URI><空格><HTTP 版本>
<字段名>:<字段值>
...
...
...
<空行>
<消息体>

(具体需要了解 HTTP 协议)

URI 就是要访问的资源的具体位置

而解析出来的服务器的名称会在字段值中出现 host : www.lab.glasscom

三、查询服务器 ip 地址

通过对网址的解析我们已经得到了想要访问的服务器的名称(域名),但我们还不能找到这台服务器。

这就像是我们知道了一个人的名字,但是名字是可以重复的,我们需要确定这个人的身份。在互联网中就是通过 ip 来标志每一台计算机的身份。

想要得到目的服务器的 ip 地址就需要向 DNS 服务器查询。

简单来说,就是向 DNS 服务器提出询问:www.lab.glasscom 的 ip 地址是什么?

然后 DNS 服务器回答:它的 ip 地址是 xx.xx.x.xx

稍微具体一点点,就是计算机一定有一个 DNS 客户端(称为 DNS 解析器),它可以跟 DNS 服务器通信。

  1. 浏览器想到得到一个网址所对应的 ip 地址的时候,就调用解析器
  2. 解析器就生成查询消息,通过操作系统的协议栈发送出去。(凡是网络通信,都需要委托给协议栈进行操作)
  3. 协议栈得到响应之后,返回信息给解析器,解析器再将 ip 地址返回给浏览器。


(这里感觉类似于函数的调用,函数中调用函数,最后将结果层层返回)

四、全世界 DNS 服务器的大接力

上面说到向 DNS 服务器查询 ip 地址,在服务器个数有限的情况下一切都合情合理,但是全世界那么多台计算机,难道全部保存在一台 DNS 服务器上面吗?如果分开保存,这台找不到的情况下又应该去哪里找呢?

域名的层次结构

域名可以是: www.zhihu.com、www.baidu.com,他们都可以看成是 .com 的手下,而他们也可以有自己的手下,比如 tieba.baidu.com 就是 www.baidu.com 的手下,这就是域名的层次结构。假设一台 DNS 服务器只能存放一个域的信息,那么就有一台服务器存放着类似于 www.zhihu.com、www.baidu.com 这样的域名的信息,但是你要问这台服务器 tieba.baidu.com 的 ip 是多少它是不知道的,不过它会告诉你 xx 服务器上存放着你想要的信息,让你去找它。

简单来说,有一台服务器存放 .com 下的域名的信息,另一台存放 .cn 下的域名的信息。当然还有很多这样的服务器,他们各自是老大,掌握了下一级别的小弟的信息,你要谁的信息就直接给你。但是大哥并不知道所有人的信息,你想要小弟的小弟的信息,他最多知道这个人是谁的小弟,然后让你去找这个人。

实际上还有一台 DNS 服务器上存放着所有老大的信息,我们可以称之为超级大佬,想要任意任意一个人的信息,问他就好了,顺藤摸瓜肯定可以找到的。

DNS 服务器的工作方式

任意一台服务器都存放着超级大佬的信息,当我们向一台 DNS 服务器发出询问时,如果它也不知道那它就会让你去问超级大佬,超级大佬会让你去问某个老大,如果那个老大知道的话就告诉你,否则就让你去问他的小弟…一直到得到想要的答案为止。

五、委托协议栈发送消息

数据收发操作概览

简单来说,数据收发就是在客户端和服务器之间建立了一条数据通道,数据就在通道里面传输。
这条数据通道并非一开始就有,而是在收发数据之前就建立起来的。收发数据大致可以分为以下 4 个阶段:

  1. 创建套接字
  2. 将管道连接到服务器端的套接字上
  3. 收发数据
  4. 断开管道并删除套接字

这里先理解成是协议栈调用 Socket 库完成上面 4 个步骤。

创建套接字阶段

套接字的创建就看成是协议栈调用 Socket 库的组件来创建套接字,创建之后会返回一个描述符,这个描述符相当于这个套接字的身份证。当要收发数据的时候,向协议栈出示描述符,协议栈就知道要对哪个套接字进行操作了。

连接阶段

连接是通过调用 Socket 库的 connect 组件完成的。需要指定以下信息:

  • 描述符
  • 服务器 ip 地址
  • 服务器端口号

描述符是告诉协议栈要对哪个套接字进行操作,ip 和端口号是用来识别对方套接字。

通信阶段

发送数据时调用 Socket 的 write 组件,需要提供描述符和要发送的数据即可。


接收数据时调用 Socket 的 read 组件,需要指定用于存放接收到的响应信息的内存地址。

断开阶段

Web 服务器发送响应消息之后主动断开连接,客户端收到断开指令后也断开连接,当浏览器调用 read 读取数据时,read 会告知浏览器收发数据操作结束,连接已经断开,然后浏览器也进入断开阶段。
(实际上双方都有可能先执行断开操作)



以前的 HTTP 协议每次获取数据都要进行所有步骤,后来在 1.1 版本中一次连接可以收发多个请求和响应,当所有数据请求完毕之后,浏览器会主动断开连接。

六、总结

一个上午的时间就这么过去了,后面的内容会越来越难,希望自己可以坚持看完。


第一章有部分知识我跳过了,学到的知识基本就是这么多了,刚才去百度了一下 C语言 Socket 编程的例子,还是觉得一脸懵逼,完全不知道说的是什么。


不管怎样,坚持看完这本书,加油!!!!