数据收发操作概览
知道了 IP 地址,就可以委托操作系统内部的协议栈向目标 IP 地址,也就是我们要访问的 Web 服务器发送消息了。和向 DNS 服务器查询 IP 地址的操作一样,这里也需要使用 Socket 库中的程序组件。不过,查询 IP 地址只需要调用一个程序组件就可以了,而这里需要按照指定的顺序调用多个程序组件
使用 Socket 库来收发数据的操作过程如图所示,简单来说,收发数据的两台计算机之间连接了一条数据通道,数据沿着这条通道流动,最终到达目的地
我们可以把数据通道想象成一条管道,建立管道的关键在于管道两端的数据出入口,这些出入口称为套接字。我们需要先创建套接字,然后再将套接字连接起来形成管道,实际过程如下:
- 服务器一方先创建套接字,进入等待状态,然后等待客户端向该套接字连接管道
- 客户端也会先创建一个套接字,然后从该套接字延伸出管道,最后管道连接到服务器端的套接字
- 当双方的套接字连接起来之后,通信准备就完成了,接下来,只要将数据送入套接字就可以收发数据了
- 当数据全部发送完毕之后,连接的管道将会被断开,断开操作可以由客户端或服务器任意一方发起,其中一方断开后,另一方也会随之断开,当管道断开后,套接字也会被删除
- 到此为止,通信操作就结束了
连接阶段:创建管道
客户端创建套接字的操作非常简单,只要调用 Socket 库中的 socket 程序组件就可以了,这里先不作详细介绍
套接字创建完成后,协议栈会返回一个描述符,应用程序会将收到的描述符存放在内存中。描述符是用来识别不同的套接字的。举个例子,打开两个浏览器窗口,同时访问两台 Web 服务器。这时,有两个数据收发操作在同时进行,也就需要创建两个不同的套接字。同一台计算机上可能同时存在多个套接字,在这样的情况下,我们就需要一种方法来识别出某个特定的套接字,这种方法就是描述符,可以将描述符理解成给某个套接字分配的编号
当创建套接字后,我们就可以使用这个套接字来执行收发数据的操作了。这时,只要我们出示描述符, 协议栈就能够判断出我们希望用哪一个套接字来连接或者收发数据了
接下来,我们需要委托协议栈将客户端创建的套接字与服务器那边的套接字连接起来。应用程序通过调用 Socket 库中的名为 connect 的程序组件来完成这一操作。当调用 connect 时,需要指定描述符、 服务器 IP 地址和端口号这三个参数
对于端口号需要解释一下,端口号与套接字联系在一起,只要指定了事先规定好的端口号,就可以连接到相应的服务器程序的套接字。这里需要与描述符作区分,描述符是和委托创建套接字的应用程序进行交互时使用的,并不是用来告诉网络连接的另一方的
通信阶段:传递消息
当套接字连接起来之后,只需要将数据送入套接字,数据就会被发送到对方的套接字中。当然,应用程序无法直接控制套接字,因此要通过 Socket 库委托协议栈来完成这个操作。这个操作需要使用 write 这个程序组件,具体过程如下:
- 应用程序在内存中准备好要发送的数据
- 调用 write 程序组件,需要指定描述符和发送数据,然后协议栈就会将数据发送到服务器
- 服务器执行接收操作,解析收到的数据内容并执行相应的操作,向客户端返回响应消息
- 当消息返回后,需要执行的是接收消息的操作,通过 Socket 库中的 read 程序组件委托协议栈来完成
- read 负责将接收的响应消息存放到接收缓冲区中,接收缓冲区是一块位于应用程序内部的内存空间,相当于转交给应用程序了
断开阶段:收发数据结束
当浏览器收到数据之后,收发数据的过程就结束了。接下来,我们需要调用 Socket 库的 close 程序组件进入断开阶段。最终,连接在套接字之间的管道会被断开,套接字本身也会被删除。断开的过程如下:
- Web 使用的 HTTP 协议规定,当服务器发送完响应消息后,应主动执行断开操作,因此服务器会调用 close 来断开连接
- 断开操作传达到客户端之后,客户端的套接字也会进入断开阶段
- 接下来,当浏览器调用 read 执行接收数据操作时,read 会告知浏览器收发数据操作已结束,连接已经断开
- 浏览器得知后,也会调用 close 进入断开阶段
|