---
TCP / IP
---

#### 目录

1. 定义
2. 特点以及优缺点
3. 应用场景
4. 报文段格式
5. 建立连接过程
6. 释放连接过程
7. 无差错传输
8. 参考

#### 定义

TCP 即传输控制协议，属于传输层通信协议，基于 TCP 的应用层协议有 HTTP、SMTP、FTP 和 POP3。

#### 特点以及优缺点

特点：面向连接、面向字节流、全双工通信、可靠

| 特点       | 具体描述                                                     |
| ---------- | ------------------------------------------------------------ |
| 面向连接   | 使用 TCP 传输数据前，必须先建立 TCP 连接；传输完成后再释放连接 |
| 全双工通信 | 建立 TCP 连接后，通信双方都能发送数据                        |
| 可靠       | 通过 TCP 连接传送的数据：不丢失、无差错、不重复、按序到达    |
| 面向字节流 | 数据以流的形式进行传输                                       |

TCP 一次传输的报文长度有限制，若太大则需要分块、分次传输，但由于 TCP 连接的可靠性，接收方可按顺序接受数据块，重新组成分块之前的数据流，所以 TCP 看起来就像直接互相传输字节流一样，即面向字节流。

优点：传输数据可靠

缺点：效率慢，因为需要建立连接，发送确认包等

#### 应用场景

应用场景即对应的应用层协议，有 HTTP/HTTPS、FTP、SMTP/POP3 协议等。

#### 报文段格式

TCP 虽然面对字节流，但传送的数据单元是报文段，报文段由首部和数据两部分组成。TCP 的全部功能体现在它首部中各字段的作用，故下面主要讲解 TCP 报文段的首部。

TCP 的首部前 20 个字符固定，后面有 4n 个字节是根据需要而增加的选项，故 TCP 首部最小长度为 20 字节。

![](https://i.loli.net/2019/02/24/5c722faed1d20.png)

| 字段               | 作用                                           | 备注                                                         |
| ------------------ | ---------------------------------------------- | ------------------------------------------------------------ |
| 序号（报文段序号） | 本报文段所发送数据的第一个字节的序号           | 4 字节                                                       |
| 确认号（ACK）      | 期望收到对方下一个报文段的第一个数据字节的序号 | 4 字节；若确认号 = N，则表明到序号 N-1 为止的所有数据都已正确收到 |
| SYN（同步位）      | 连接建立时用于同步序号                         | SYN = 1、ACK = 0，表明这是一个连接请求报文段；SYN = 1，ACK = 1，表明这是一个连接请求响应报文段 |
| FIN（终止控制位）  | 释放连接                                       | FIN = 1，表明此报文段的发送方已发送数据完毕，要求释放连接    |

#### 建立连接过程

TCP 建立连接需要三次握手。具体如下：

![](https://i.loli.net/2019/02/24/5c72336bdde32.png)

建立 TCP 连接前，TCP 客户端、服务端都处于关闭状态，直到客户端主动打开链接，服务端才被动打开链接，等待接受客户端的请求。

| 过程       | 具体描述                                                     | 报文段信息                                                   | 状态                                                 |
| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ---------------------------------------------------- |
| 第一次握手 | 客户端向服务器发送一个连接请求的报文段                       | 同步标识位 SYN = 1，随机选择一个起始序列号 seq = x，不携带数据（因 SYN 位被设置为 1 的报文段不能携带数据，但要消耗一个序号） | 客户端进入同步已发送状态（SYN_SNED），等待服务器确认 |
| 第二次握手 | 服务器收到请求连接报文段后，若同意建立连接，则向服务端发回连接确认的报文段（为该 TCP 连接分配 TCP 缓存、变量） | 同步标志位 SYN = 1，确认标志位 ACK = 1，随机选择一个起始序号 seq = y，确认号字段设为 ack = x + 1，不携带数据 | 服务器进入同步已接受状态（SYN_RCVD）                 |
| 第三次握手 | 客户端收到确认报文段后，向服务器再次发出连接确认报文段（为该 TCP 连接分配 TCP 缓存、变量） | 确认标记位 ACK 设为 1，序号 seq = x +1，确认号字段 ack = y + 1，可携带数据（因 SYN 没有设置为 1，若不携带数据则不消耗序号） | 客户端、服务端都进入已创建状态（可开始发送数据）     |

成功进行 TCP 的三次握手之后，就建立起了一条 TCP 连接，即可传送应用层数据。

因 TCP 提供的是全双工通信，故通信双方的应用进程在任何时候都能发送数据，三次握手期间，任何一次未收到对面的回复，则都会重发。

**为什么 TCP 建立连接需要三次握手呢？**

是因为防止服务器端因接收了早已失效的连接请求报文，从而一直等待客户端请求，最终导致形成死锁、浪费资源。

客户端发出的第一个连接请求报文段无丢失，而是在某个网络节点长时间滞留了，导致延误到连接释放后的某个时间才到达服务器，但是服务端并不知道这是一个早已失效的报文段，于是仍然向客户端发出了确认报文段，同意建立 TCP 连接，这时候服务端就一直等待客户端再次发起请求报文段。但是对于客户端来说，报文已经失效了就不会向服务端发送数据。

采用三次握手，服务端由于收不到客户端的确认信息，即知道客户端并无要求建立 TCP 连接，故服务器也就不会一直等待客户端发送数据。

SYN 洪泛攻击：

从上面可看出，服务端的 TCP 资源分配时刻是在完成第二次握手时，而客户端的 TCP 资源分配是在完成第三次握手时，这就使得服务端容易受 SYN 洪泛攻击，即同时多个客户端发起连接请求，从而需进行多个请求的 TCP 连接资源分配。

#### 释放连接过程

在通信结束后，双方都可以释放连接，共需四次挥手。

![](https://i.loli.net/2019/02/24/5c723ba5d642a.png)

释放 TCP 连接前，客户端和服务端都处于已创建状态，直到客户端主动关闭 TCP 连接。

| 过程       | 具体描述                                                     | 报文段信息                                                   | 状态                                                         |
| ---------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------------------------------------------ |
| 第一次挥手 | 客户端向服务端发送一个连接释放的报文段（停止再发送数据）     | 终止控制位 FIN = 1，报文段序号设为前面传送数据最后一个字节的序号加一：seq = u，可携带数据（FIN = 1 的报文即使不携带数据也消耗一个序号） | 客户端进入终止等待状态（FIN_WAIT_1）等待服务端的确认         |
| 第二次挥手 | 服务端接收到连接释放报文段后，则向客户端发回连接释放确认的报文段 | 确认标记位 ACK = 1，报文序列号设为前面传送数据最后一个字节的序号加一：seq = v，确认号字段 ack = u + 1 | 服务端进入关闭等待状态（CLOSE_WAIT），客户端收到服务端的确认后，进入终止等待 2 状态（FIN_WAIT - 2），等待服务器发出释放连接请求；至此，客户端到服务端的 TCP 连接已经断开，但是服务端到客户端的连接未断开 |
| 第三次挥手 | 若服务端已无要向客户端发送数据，则发出释放连接的报文段       | 终止控制位 FIN = 1，确认标记位 ACK  = 1，报文段序号 seq = w，重复上次已发送的确认号字段 ack = u + 1，可携带数据 | 服务端进入最后确认状态（LAST-ACK）                           |
| 第四次挥手 | 客户端收到连接释放报文段后，则向服务端发回连接释放确认的报文段 | 确认标记位 ACK = 1，报文段序号 seq = u + 1，确认号字段 ack = w + 1，可携带数据 | 客户端进入时间等待状态（TIME-WAIT），服务端进入关闭状态（CLOSED）；此时 TCP 连接还未释放，须经过时间等待计时器设置的时间 2MSL 后，客户端才进入连接关闭状态（CLOSED），即服务端进入关闭状态比客户端要早一些 |

**为什么 TCP 释放连接需要四次挥手？**

这是为了保证通信双方都能通知对方需断开连接，断开连接后，都无法接受或发送消息给对方。

为什么客户端关闭连接前需要等待 2MSL 的时间？即 TIME_WAIT 的作用是什么？

MSL 即最长报文段寿命，它的存在是为了保证客户端发送的最后一个连接释放确认报文能到达服务器，从而使得服务器能正常释放连接。

客户端发送的最后一个连接释放确认报文，可能会丢失，当服务器收不到最后一个连接释放确认报文时则不会进入关闭状态，但是会超时重发连接释放报文。如果客户端不等待 2MSL 时间就直接进入关闭状态，当最后一个连接释放确认报文丢失，服务端重发释放报文时，客户端则无法接受到这个报文，因此也就无法发送连接释放确认报文，最终导致服务端无法进入关闭状态。

同时，还可以防止上文提到的早已失效的连接请求报文出现在本连接中，客户端发送了最后一个连接释放请求确认报文后，再经过 2MSL 时候，则可使本连接持续时间内所产生的所有报文段都从网络中消失，即在下一个新的连接中就不会出现早已失效的连接请求报文。

#### 无差错传输

无差错即传输信道不出差错，同时发送和接受效率匹配，无论发送方以多快的速度发送数据，接收方总来得及处理收到的数据。

#### 参考

[计算机网络：这是一份全面 & 详细 的TCP协议学习指南](https://blog.csdn.net/carson_ho/article/details/85316719)