一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第8天,点击查看活动详情。
往期文章
- 网络是怎样连接的(一)—— 浏览器访问 Web 服务器过程概览
- 网络是怎样连接的(二)—— 浏览器生成 HTTP 消息
- 网络是怎样连接的(三)—— 通过 DNS 服务器查询 IP 地址
- 网络是怎样连接的(四)—— DNS 服务器工作介绍
- 网络是怎样连接的(五)—— 委托操作系统进行收发消息过程概览
- 网络是怎样连接的(六)—— 协议栈内部探索步骤
- 网络是怎样连接的(七)—— 协议栈的内部结构
- 网络是怎样连接的(八)—— 探索套接字
- 网络是怎样连接的(九)—— 连接连的到底是啥
前言
在上一篇文章中,我们了解了 连接实际上是通信双方交换控制信息,在套接字中记录这些必要信息并准备数据收发的一连串操作,那么今天我们就来看看连接的实际过程。
连接的过程其实就是我们经常提起的 TCP 三次握手,不过本文中我们讲的不会特别细,因为我们本着由浅入深的原则一点点学习,所以本文对于连接的过程讲的会更通俗易懂,想要了解详细的连接建立过程,可以看我的另一篇文章:TCP 三次握手和四次挥手
连接操作的实际过程
连接的过程是从应用程序调用 Socket 库的 connect 方法开始的。
connect(<描述符>,<ip>,<port>,...)
上面的调用提供了服务器的 IP 地址和端口号,这些信息会传递给协议栈中的 TCP 模块,然后,TCP 模块会与该 IP 地址对应的对象,也就是与服务器的 TCP 模块交互控制信息。这一交互过程的步骤如下。
1. 创建 TCP 头部
连接操作的第一步是在 TCP 模块出创建表示连接控制信息的头部。
首先,客户端先创建一个包含表示开始数据收发操作的控制信息的头部,具体如下图所示。头部包含很多字段,我们暂时先重点关注发送方和接收方的端口号。
到这里,客户端(发送方)的套接字就准确找到了服务器(接收方)的套接字,也就搞清楚了我应该连接哪个套接字。
然后,我们将头部中的控制位的 SYN 设置为 1,这个字段表示建立连接。此外我们还需要设置适当的需要和窗口大小,这一点我们会稍后详细讲解。

2. IP 模块发送数据
当 TCP 头部创建好之后,接下来 TCP 模块会将信息传递给 IP 模块并委托它进行发送。
IP 模块执行网络包发送操作后,网络包就会通过网络到达服务器,然后服务器上的 IP 模块会将接收到的数据传递给 TCP 模块,服务器的 TCP 模块根据 TCP 投不中的信息找到端口号对应的套接字,也就是说,从处于等待连接状态的套接字中找到与 TCP 投不中记录的端口号相同的套接字就可以了。
3. 服务器接收数据并返回响应信息
当找到对应的套接字之后,套接字中会写入响应的信息,并将状态改为正在连接。上述操作完成后,服务器的 TCP 模块会返回响应,这个过程和客户端一样,需要在 TCP 头部中知识发送方和接收方的端口号以及 SYN 字段。此外,在返回响应时,还需要将 ACK 控制位设为 1,这表示已经接收到相应的网络包。
网络中经常会发生错误,网络包也会发生丢失,因此双方在通信时必须相互确认网络包是否已经送达,而设置 ACK 标志位就是用来进行这一确认的。接下来,服务器 TCP 模块会将 TCP 头部传递给 IP 模块,并委托 IP 模块向客户端返回响应。
4. 客户端处理响应数据
接下来,网络包就会返回到客户端,通过 IP 模块到达 TCP 模块,并通过 TCP 的头部的信息确认链接服务器的操作是否成功。
如果 SYN 为 1 则表示连接成功,这是会向套接字中写入服务器的 IP 地址、端口号等信息,同时还会将状态改为连接完毕。
5. 客户端发送确认信息
处理完响应数据之后,客户端还剩下最后一个步骤。刚才服务器返回响应时将 ACK 标志位设置为 1,相应的,客户端也需要将 ACK 标志位设置为 1 并发送回服务器,告诉服务器刚才的响应包已经收到。当这个服务器收到这个返回包之后,链接操作才算全部完成。
现在,套接字就已经进入随时可以收发数据的状态了。
建立连接之后,协议栈的链接操作就结束了,也就是说 connect 已经执行完毕,控制流程被交回到应用程序。
控制信息的分类
看完了连接过程,我们再来学习一个知识点,我们一直提到控制信息都有哪些呢?实际上可以分为两类:
- 头部中记录的信息
- 套接字(协议栈中的内存空间)中记录的信息
1. 头部中记录的信息
第一类是客户端和服务器相互联络时交换的控制信息。
这些信息不仅连接时需要,包括数据收发和断开连接操作在内,整个通信过程都需要,这些内容在 TCP 协议的规格中进行了定义。具体字段如上图所示,这些字段是固定的,在连接、收发、断开等各个阶段中,每次客户端和服务器之间进行通信时,都需要提供这些控制信息。
具体来说,这些信息会被添加在客户端与服务器之间传递的网络包的开头。在连接阶段,由于数据收发还没有开始,网络包中没有实际的数据,只有控制信息。这些控制信息位于网络包的开头,因此被称为头部。此外,以太网和 IP 协议也有自己的控制信息,这些信息也叫头部,为了避免各种不同的头部发生混淆,我们一般会记作 TCP 头部、以太网头部、IP 头部。头部结构如下图所示:

客户端和服务器在通信中会将必要的信息记录在头部并相互确认。
头部的信息非常重要,理解了头部各字段的含义,就等于理解了整个通信的过程。
2. 套接字(协议栈中的内存空间)中记录的信息
第二类是保存在套接字中,用来控制协议栈操作的信息。
应用程序传递来的信息以及从通信对象接收到的信息都会保存在这里,还有收发数据操作的执行状态等信息也会保存在这里,协议栈会根据这些信息来执行每一步的操作;
我们可以说,套接字的控制信息和协议栈的程序本身其实是一体的。因此,“协议栈具体需要哪些信息”会根据协议栈本身的实现方式不同而不同。
但这个并没有什么问题,因为协议栈中的控制信息通信双方是看不见的,只要在通信时按照规则将必要的信息写入头部,客户端和服务器之间的通信就能够得以成立。例如,Windows 和 Linux 操作系统的内部结构不同,协议栈的实现方式不同,必要的控制信息也就不同。但即便如此,两种系统之间依然能够相互通信,同样的,计算机和手机之间也能互相通信。
正如前面所说,协议栈的实现不同,因此我们无法具体说明协议栈里到底保存了哪些控制信息,但可以用命令来显示一些重要的套接字控制信息,这些信息无论何种操作系统的协议栈都是共同的,通过理解这些重要信息,就能够理解协议栈的工作方式了。
在上一篇文章中通过 netstat 命令看到的信息就是这些通用的控制信息了。
总结
今天我们了解了链接操作的实际过程,其中:
连接操作的第一步是在 TCP 模块出创建表示连接控制信息的头部。
通过 TCP 头部中的发送方和接收方端口号可以找到要连接的套接字。
通信操作中使用的控制信息分为两类:
- 第一类是客户端和服务器相互联络时交换的控制信息。
- 第二类是保存在套接字中,用来控制协议栈操作的信息。
参考文章
- 《网络是怎样连接的》—— 户根勤