TCP 协议如何建立连接

TCP 与 UDP 都是计算机网络 OSI 模型中的传输层协议,它们都提供了端到端的数据通信能力。其中 UDP 协议是一种无连接、无状态的协议,而 TCP 协议则是一个全双工的面向连接的协议。

Prologue

​ TCP 是一种面向连接的协议,它通过在两个通信端点之间建立持久的连接来实现全双工通信。在 TCP 连接中,数据可以双向流动,每一方都可以独立地发送和接收数据。TCP 提供可靠的、面向连接的通信,确保数据的顺序和可靠性。由于 TCP 的连接是全双工的,客户端和服务器之间可以独立地发送和接收数据。下面,让我们详细聊聊 TCP 协议。

TCP 协议在数据传输的作用

​ 要说为什么 TCP 协议需要建立连接,我们先要明白 TCP 协议在数据传输中的作用:

post_how_tcp_establish_connection_1

​ 还是以我们日常开发与生活中中经常访问的网页为例:浏览器发起一个 HTTP GET 请求的时候,请求将通过 IP 与 MAC 层的协同作用,找到访问目标资源的最佳路径。

post_how_tcp_establish_connection_2

​ 首先,在 HTTP 层,消息数据将被构建,其中包含用户请求的数据或需要传输的内容。接着,在 TCP 层,每个数据段都会附加一个包含了 TCP 协议相关的控制信息的 TCP 头部。然后,在 IP 层, IP 头部被添加,该头部包含了源地址和目标地址等 IP 协议的信息。最后,在 MAC 层,帧头部被添加,包含了 MAC 地址等物理层的信息。

post_how_tcp_establish_connection_3

​ 当路由器接收到数据帧后,会解析其中的 MAC 头部与 IP 头部,并由此确定数据帧的目的地。在确定数据帧的下一跳之后,路由器将重新附加相应的 IP 头部与 MAC 头部,并把数据帧发送交换机。接下来,交换机根据 MAC 地址把数据帧发送到目标主机。目标主机接收到数据后,逐层解析各个协议层的头部信息,包括 MAC 头部、IP 头部和 TCP 头部。在 TCP 层,目标主机接收到的数据段重新组装成完整的请求。最终,整个消息层层上升,达到最顶层即应用层的 HTTP 层。

​ 在上述过程中,以 TCP 协议所在的传输层为分割线:在传输层以下,IP 层和 MAC 层选择路由。在 TCP 层以上,HTTP 层构建请求和响应消息。

​ 综上。TCP 将可变长度的请求转换为多个段,并确保所有部分按其原始顺序到达。这就是 TCP 的作用。

TCP 头部的格式

​ 在进入本篇的正题 TCP 协议握手之前,还需要介绍一下它 Header 的格式。

post_how_tcp_establish_connection_4

​ 如上图所示,前两个字段是 源端口目标端口源端口 表示数据包的来源,而“目标端口”表示其的目的地。

​ 以访问本站为例:打开浏览器控制台,选择 Network,刷新页面,点击任意一个网络请求,可以看到,ixjs.com 这个网址在 DNS 解析之后,被转换成 IP 地址 43.138.16.18 ,而 IP 地址冒号后面的数字,所代表的就是刚刚提到的 目标端口

​ 下面的部分是序列码与 ACK 确认码,序列码用于标识数据包。确认码用于确认数据接受成功。TCP 选项是可选的,用于提供额外的信息。

TCP 的如何建立连接

​ 终于,我们进入本篇要讲的正题:TCP 协议是如何建立连接的。

​ TCP 协议需要两个通信端点之间发送三次消息来建立连接,这个过程通常被称为“三次握手”。发送的消息中,主要可以分为以下两类:

  • 同步初始序列号(ISN):确保通信的双方使用相同的起始序列号,以协调数据传输的顺序。
  • 交换参数(例如窗口缩放因子、最大分段大小、选择性确认算法等):确定连接双方在数据传输中采用的一些参数,以优化通信的效率。
post_how_tcp_establish_connection_5

​ 三次握手的步骤如下:

  • 客户端发送一个 SYN 包,表示请求建立连接
  • 服务器回复一个 SYN/ACK 包,表示同意建立连接
  • 客户端再回应一个 ACK 包,确认连接已建立

The 1st Handshake

post_how_tcp_establish_connection_6

​ 客户端发起第一次握手,通过发送包含 初始序列号标志位 的 SYN 包来建立连接。

初始序列号 ISN

初始序列号 是一个随机值,不允许为零。

标志位

标志位 用于标记数据段的类型。如图所示,第一次握手时,SYN 类型的标志被设置为 1,而其他标志被保持为 0,表示这是一个 SYN 消息。

post_how_tcp_establish_connection_7

​ 如图,以访问本站为例,当在浏览器输入链接并按下回车后,ixjs.com 的域名经过 DNS 解析后,向 IP 地址 43.138.16.18 发起了网络请求,建立连接。我们可以清晰的看到 SYN 包的 ISN 序列号与 SYN 标志。

The 2nd Handshake

post_how_tcp_establish_connection_8

​ 在接收到客户端的 SYN 包后,服务器将通过进行第二次握手来做出响应,即回送一个 SYN/ACK 包作为响应。

序列号

​ 这次我们将发送到两个序列号:

  • 服务器的 初始序列号 (ISN):服务器的 初始序列号 是一个随机数。(与客户端刚刚发送的初始序列号不同)
  • 确认码 :基于客户端的初始序列号 生成,具体为
服务器的确认序列号 = 1 + 客户端的初始序列号

标志位

​ 在标志位中,ACK 和 SYN 都被置为 1。

post_how_tcp_establish_connection_9

​ 如上图,可以看到第二次握手中服务器回送的 ISN 与 ACK 的序列号与标志位。

The 3rd Handshake

post_how_tcp_establish_connection_10

​ 客户端收到服务器第二次握手发来的 SYN/ACK 包后,再次向服务器发送 ACK 包。这就是第三次握手。

序列号

​ 客户端将响应一个确认码,与之前相同,响应的确认码也基于服务器发送的初始序列号 ISN 生成的:

客户端的确认序列号 = 1 + 服务器的初始序列号

标志位

​ 只有 ACK 被置为 1

post_how_tcp_establish_connection_11

​ 如图例,我们可以看到客户端返回的 ACK 包的确认码与标志位。

TCP 连接期间的状态变化

post_how_tcp_establish_connection_12

​ 最初,两端都处于关闭状态。为了提供服务,我们需要先开启服务端的对应端口,例如 HTTP 协议的 80 端口或者是 HTTPS 协议的 443 端口,此时,服务端进入了监听状态。

​ 在第一次握手中,浏览器发送了一个 SYN 包,随即,客户端的 TCP 状态变为 SYN-Sent 状态。

post_how_tcp_establish_connection_13

​ 当服务器收到 SYN 包时,会将 SYN 包放入 SYN 队列中,服务器的 TCP 状态进入 SYN-Received 状态。然后,服务器发送 SYN/ACK 包,也就是第二次握手。

​ 浏览器接收到消息,其 TCP 进入已建立状态,并向服务器发送最后的 ACK 包。

​ 服务器收到 ACK 包后,会把请求帧从 SYN 队列中移出,放入 ACCEPT 队列中。此时连接便建立了。如果有类似 Nginx 之类的代理服务器,则连接实际上是由代理服务器处理的。

查看服务器建立的所有连接

​ 建立了 TCP 链接后,在服务器里可以使用我们比较熟悉的 netstat 工具查看所有已经建立的 TCP 连接。

post_how_tcp_establish_connection_14

​ 我们使用了 netstat 展示了所有活动连接和侦听端口,而且以数字形式显示IP地址和端口号,并过滤了其中状态为 ESTABLISHED 的连接。可以看到,我们使用了 63534 端口与服务器的 443 端口建立了 TCP 连接。其中,10.0.8.6:443 展示的是服务器的本机地址。具体可以通过 ifconfig 查看 eth0

Epilogue

​ TCP 承担了确保消息可靠传递的任务,而其他网络层不太关心这个问题。为了履行这一职责,需要建立一个连接,为了建立连接,TCP采用了一种称为 “三次握手” 的过程。而握手过程中的序列号、确认号以及标志位是理解这一过程的核心,因为每次握手都导致客户端和服务器端TCP连接状态的变化。

滚动至顶部