一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情。
往期文章
- 网络是怎样连接的(一)—— 浏览器访问 Web 服务器过程概览
- 网络是怎样连接的(二)—— 浏览器生成 HTTP 消息
- 网络是怎样连接的(三)—— 通过 DNS 服务器查询 IP 地址
- 网络是怎样连接的(四)—— DNS 服务器工作介绍
前言
在之前的文章中,我们讲述了浏览器是如何生成 HTTP 消息,并且通过 DNS 服务器获取目标的 IP 地址。
消息、目标地址都有了,此时我们要做的就是将消息发送出去。消息的发出需要委托协议栈来实现,操作系统调用 Socket 库来完成消息的发送与接收。
今天我们的目标就是来了解一下这个 数据“委托”操作的整体流程,对于协议栈收到委托之后具体是如何操作的,我们会在之后的学习中继续探讨。今天主要学习的是整体流程,当我们对整体流程有了初步了解之后,我们才会对其底层细节及其实现有更深的认识。
下面就开始我们今天的学习之旅。
名词解释
协议栈
协议栈(Protocol stack),又称协议堆叠,是计算机网络协议套件的一个具体的软件实现。协议套件中的一个协议通常是只为一个目的而设计的,这样可以使得设计更容易。因为每个协议模块通常都要和上下两个其他协议模块通信,它们通常可以想象成是协议栈中的层。最低级的协议总是描述与硬件的物理交互,每个高级的层次增加更多的特性。用户应用程序只是处理最上层的协议。
数据收发操作概览
因为要发送给 Web 服务器的 HTTP 消息实际上市一种数字消息,所以需要协议栈完成消息发送。收发数字信息这一操作不仅限于浏览器,对于各种使用网络的应用程序来说都是共通的。
这个发送消息的过程同向 DNS 服务器查询 IP 地址的操作类似,也需要使用 Socket 库中的程序组件。不过发送消息的过程比较复杂,需要 按照指定的顺序调用多个程序组件。接下来我们来探索的就是这一过程。
使用 Socket 库来收发数据的操作可以简化类似上图。收发数据的两台计算机之间连接这一条“数据通道”,数据沿着这条通道流动,最终到达目的地。我们可以把数据通道想象成一条管道,将数据从一端送入管道,数据就会到达管道的另一端然后被取出。数据可以从任何一端被送入管道,数据的流向是双向的。
当然,这条管道实际上是并不存在的。只是为了能够让我们更好的理解而想象出来的一条管道。而且这个管道并不是一开始就有,在进行收发数据操作之前,双方需要先建立这条通道才行。建立通道的关键在于管道两端的数据出入口,这些出入口称为套接字。 我们需要先创建套接字,然后再将套接字连接起来形成管道。
实际的过程如下:首先,服务器一方先创建套接字,然后等待客户端向该套接字连接管道。当服务器进入等待状态时,客户端就可以连接管道了。具体来说,客户端会先创建一个套接字,然后从该套接字延伸出管道,最后管道连接到服务器的套接字上。当双方的套接字连接起来之后,通信准备就完成了。接下来,只要将数据送入套接字就可以收发数据了。
我们再来看一看收发数据操作结束时的情形。当数据全部发送完毕之后,连接的管道将会被断开。管道在连接时是由客户端发起的,但在断开时可以由客户端或服务器任意一方发起。其中一方断开后,另一方也会随之断开,当管道断开后,套接字也会被删除。到此为止,通信操作就结束了。
综上所述,收发数据的操作分为若干个阶段,可以大致总结为以下 4 个:
- 创建套接字(创建套接字阶段) :
- 将管道连接到服务器端的套接字上(连接阶段) :
- 收发数据(通信阶段) :
- 断开管道并杉树套接字(断开阶段) :
总结
总结一下就是:在上述的每个阶段,Socket 库中程序组件都会被调用来执行相关的数据收发操作。同样再来强调一下:上述这 4 个操作都是由操作系统中的协议栈来执行的,浏览器等应用程序并不会自己去做连接管道、放入数据这些操作。而是委托协议栈来代劳。本文中主要介绍的只是这个“委托”操作。 关于协议栈收到委托之后具体是如何连接管道和放入数据的。我们会在之后的学习中再去介绍。
此外,这些委托的操作都是通过调用 Socket 库中的程序组件来执行的,但这些数据通信用的程序组件其实仅仅充当了一个桥梁的角色,并不执行任何实质性的操作,应用程序的委托内容最终会被原原本本的传递给协议栈。在这里我们可以把 Socket 库和协议栈看成一个整体,这样会更容易理解一些。
数据收发操作步骤详细描述
对于上面的提到的收发数据的四个步骤,在这里我们来详细介绍一下每个步骤都做了些什么工作。说是详细介绍,其实仅仅是描述了做了哪些事,对于实现细节,我们在接下来的文章中再去介绍。
创建套接字
创建套接字的方式即调用 Socket 库中的特定程序组件。
套接字常见完成后,协议栈会返回一个描述符,应用程序会将收到的描述符存放在内存中。 我们可以把描述符理解为是用来识别不同的套接字的。一台计算机上可能同时存在多个套接字,在这样的情况下,我们就需要一种方法来识别出某个特定的套接字,这种方法就是描述符。
当创建套接字后,我们就可以使用这个套接字来执行收发数据的操作。这时,只要我们出示描述符,协议栈就能够判断出我们希望用哪一个套接字来连接或者收发数据了。
连接阶段:把管道接上去
接下来,我们需要委托协议栈将客户端创建的套接字与服务端那边的套接字连接起来。应用程序通过调用 Socket 库中的名为 connect 的程序组件来完成这一操作。当我们调用 connect 时,需要执行三个参数,即 描述符、服务器 IP 地址、端口号:
- 描述符:即创建套接字的时候由协议栈返回的那个操作符。connect 会将应用程序指定的描述符告知协议栈,然后协议栈根据这个描述符来判断到底使用哪一个套接字去和服务端的套接字进行连接,并执行连接的操作;
- 服务器 IP 地址:即通过 DNS 服务器查询得到的我们要访问的服务器的 IP 地址。在之前的文章中,我们已经讲过了,在进行数据收发操作时,双方必须知道对方的 IP 地址并告知协议栈;
- 端口号:IP 地址的作用是用来识别哪台计算机,但是要连接计算机上的哪个套接字就需要端口号这个参数了。当我们同时指定 IP 地址和端口号时,就可以明确识别出某台计算机上的某个具体的套接字;常用的端口号如 Web 是 80 号,电子邮件是 25 号等等;
我们再来思考一个问题:能不能用前面创建套接字时提到的那个描述符来识别套接字呢?
这种方式是不可以的。因为描述符是和委托创建套接字的应用程序进行交互时使用的,并不是用来告诉网络连接的另一方的,因为另一方并不知道这个描述符。同样地,客户端也无法知道服务器上的描述符。因此,客户端也无法通过服务端的描述无去确定位于服务器上的某一个套接字。所以,我们需要另外一个对客户端也同样适用的机制,而这个机制就是端口号。
如果说描述符是用来在一台计算机内部识别套接字的机制,那么端口号就是用来让通信的另一方能够识别出套接字的机制。
通信阶段:传递消息
当套接字连接起来之后,我们就可以通过套接字来发送数据了。
首先,应用程序需要在内存中准备好要发送的数据。根据用户输入的网址生成的 HTTP 请求消息就是我们要发送的数据。接下来,应用程序调用 Socket 库中的 write 程序组件发送数据,调用 write 时,需要指定描述符和发送数据。然后协议栈就会将数据发送到服务器。由于套接字中已经保存了已链接的通信对象的相关信息,所以只要通过描述符指定套接字,就可以识别出通信对象,并向其发送数据。接着,发送数据会通过网络到达我们要访问的服务器。
接下来,服务器执行接收操作,解析收到的数据内容并执行相应的操作,向客户端返回响应消息。
当消息返回后,需要执行的是接收消息的操作。接收消息的操作是通过 Socket 库中的 read 程序组件委托协议栈来完成的。调用 read 时需要指定用于存放接收到的响应消息的内存地址,这一内存地址称为接收缓冲区。于是,当服务器返回响应消息时,read 就会负责将接收到的响应消息存放到接收缓冲区中。由于接收缓冲区是一块位于应用程序内部的内存空间,因此当消息被存放到接收缓冲区中时,就相当于已经转交给了应用程序。
断开阶段:收发数据结束
当浏览器收到数据之后,收发数据的过程就结束了。接下来,我们需要调用 Socket 库中的 close 程序组件进入断开阶段。最终,连接在套接字之间的管道会被断开。套接字本身也会被删除。
断开的过程如下。Web 使用的 HTTP 协议规定,当 Web 服务器发送完响应消息之后,应该账户东执行断开操作,因此 Web 服务器会首先调用 close 来断开连接。断开操作传达到客户端之后,客户端的套接字也会进入断开阶段。接下来,当浏览器调用 read 执行接收数据操作时,read 会告知浏览器收发数据操作已结束,连接已经断开。浏览器得知后,也会调用 close 进入断开阶段。
这就是 HTTP 的工作过程。事实上 HTTP 协议将 HTML 文档和图片都作为单独的对象来处理,每获取一次数据,就要执行一次连接、发送请求消息、接收响应消息、断开的过程。因此,如果一个网页中包含很多张图片,就必须重复进行很多次连接、收发数据、断开的操作。对于同一台服务器来说,重复连接和断开显然是效率很低的,因此后台人们有设计出了能够在一次连接中收发多个请求和响应的方法。在 HTTP1.1 中就可以使用这种方法,在这种情况下,当所有数据都请求完成后,浏览器会主动触发断开连接的操作。
总结
在本篇文章中,我们探索了浏览器和 Web 服务器之间收发消息的过程。浏览器等应用程序并不会自己去做连接管道、放入数据这些操作。而是委托协议栈来代劳。
收发消息主要分为 4 步:
- 创建套接字
- 将管道连接到服务器端的套接字上
- 收发数据
- 断开管道并删除套接字
对于每步的详细介绍,我们会在接下来的文章逐步向大家讲述,希望能对大家有所帮助!
参考文档
- 《网络是怎样连接的》—— 户根勤