[TOC] #### 1. TCP 是什么 --- 关于 TCP 是什么,可以从核心定义、主要特性以及应用场景三个层面来概括 一、核心定义:它是传输层的 “基石” TCP(Transmission Control Protocol,传输控制协议)是一种面向连接的、可靠的、基于字节流的传输层通信协议 它位于 OSI 七层模型的传输层(或 TCP/IP 模型的网络接口层之上),主要负责在两台主机之间提供端到端的数据传输服务 二、核心特性:可靠、有序、全双工 + 面向连接:在传输数据前,必须通过 “三次握手” 建立连接,通信结束后通过 “四次挥手” 释放连接 + 高可靠性:通过校验和、序列号、确认应答(ACK)、超时重传等机制,确保数据无差错、不丢失、不重复且按序到达 + 面向字节流:TCP 把数据看作一连串无结构的字节流,而不是一个个独立的数据包,它会根据网络状况自动进行报文段的拆分与重组 + 全双工通信:通信双方在任何时刻都能同时发送和接收数据 + 拥塞控制与流量控制:通过滑动窗口、慢启动、拥塞避免等算法,防止网络过载,保证网络的公平性 三、应用场景:对准确性要求高的业务 正因为 TCP 保证了数据的绝对可靠,它被广泛应用于 HTTP/HTTPS(Web 浏览)、FTP(文件传输)、SMTP/POP3(电子邮件)等不允许数据丢失的场景 在软件开发中,主要有以下几类请求是基于 TCP 协议的: > Web 业务请求(HTTP / HTTPS) 比如浏览器访问页面、前后端分离架构下的 AJAX/Fetch 请求、或者微服务之间的 RESTful API 调用 + HTTP:明文传输,底层直接跑在 TCP 上(默认端口 80) + HTTPS:在 HTTP 和 TCP 之间加入了 TLS/SSL 加密层,但底层依然是 TCP(默认端口 443) > 数据库交互请求 在我们的 PHP 后端业务中,与数据库的通信全部是基于 TCP 的 + MySQL / MariaDB:PHP 通过 PDO 或 MySQLi 扩展连接数据库,底层走的是 TCP 协议(默认端口 3306) + PostgreSQL / Oracle:同样基于 TCP(默认端口 5432 / 1521) > 缓存与消息队列请求 现代高并发架构中必不可少的中间件,也全部依赖 TCP 来保证数据不丢失 + Redis:PHP 通过 phpredis 或 Predis 扩展与 Redis 通信,底层是 TCP(默认端口 6379) + RabbitMQ / Kafka / RocketMQ:服务间的异步解耦、削峰填谷,消息的生产和消费底层也是 TCP > 其它特定业务协议 + FTP / SFTP:用于文件上传下载(默认端口 21/22) + SMTP / POP3 / IMAP:用于邮件发送和接收(默认端口 25/110/143) + RPC 框架:如 gRPC、Thrift 等,微服务之间的高性能调用也是基于 TCP #### 2. TCP 三次握手流程 --- 第一次握手:客户端发起请求(Client -> Server) + 动作:客户端向服务端发送一个 SYN(同步)报文段 + 关键字段:此时报文中 `SYN=1`,并且客户端会随机生成一个初始序列号 `seq=x` + 状态变化:发送完这个报文后,客户端进入 `SYN_SENT`(同步已发送)状态,等待服务端的确认 第二次握手:服务端确认并回应(Server -> Client) + 动作:服务端收到 SYN 报文后,如果同意建立连接,会回复一个 SYN+ACK 报文段 + 关键字段:报文中 SYN=1,ACK=1。同时包含服务端自己的初始序列号 seq=y,以及对客户端的确认应答号 ack=x+1 + 状态变化:服务端进入 `SYN_RCVD`(同步收到)状态,等待客户端的最终确认 第三次握手:客户端最终确认(Client -> Server) + 动作:客户端收到服务端的确认后,会发送一个 ACK 报文段 + 关键字段:报文中 ACK=1,包含客户端的序列号 seq=x+1,以及对服务端的确认应答号 ack=y+1 + 状态变化:客户端发送后进入 ESTABLISHED(已建立连接)状态。服务端收到这个 ACK 报文后,也进入 ESTABLISHED 状态。至此,三次握手完成,双方可以开始传输数据 简单来说:TCP 三次握手就是客户端和服务器通过交换三个报文段来建立连接的过程,完成后双方才能开始可靠传输数据 #### 3. TCP 为什么要三次握手 --- 最核心的原因可以总结为:两个确认、一个防范 一、两个确认(确保双方通信能力) + 第一次和第二次握手:服务端确认了客户端的 “发送能力”,客户端确认了服务端的 “接收和发送能力” + 第三次握手:服务端确认了客户端的 “接收能力”,只有经过这三次交互,双方的收发能力才得到了双向确认 二、一个防范(防止历史连接导致资源浪费) 这是面试中最关键的考点:为什么不能是两次 ? 如果只有两次握手,当网络中滞留的 “历史旧 SYN 报文” 到达服务端时,服务端会直接分配资源并进入 ESTABLISHED 状态 但客户端其实并没有发起请求,收到回应后会直接丢弃,导致服务端资源白白浪费,甚至引发 DoS 攻击 有了第三次握手,服务端在收到 ACK 之前只处于 SYN_RCVD 状态,不会分配完整资源,从而完美防范了这个问题