## TCP三次握手与四次挥手
![](https://img.kancloud.cn/c4/79/c479d4ecefd141aa70c025eb9fbc82cb_1556x1350.png)
### 三次握手
#### 为什么连接建立需要三次握手,而不是两次握手?
**防止失效的[连接请求报文](https://www.zhihu.com/search?q=%E8%BF%9E%E6%8E%A5%E8%AF%B7%E6%B1%82%E6%8A%A5%E6%96%87&search_source=Entity&hybrid_search_source=Entity&hybrid_search_extra=%7B%22sourceType%22%3A%22answer%22%2C%22sourceId%22%3A254224088%7D)段被服务端接收,从而产生错误。**
PS:失效的连接请求:若客户端向服务端发送的连接请求丢失,客户端等待应答超时后就会再次发送连接请求,此时,上一个连接请求就是『失效的』。
若建立连接只需两次握手,客户端并没有太大的变化,仍然需要获得服务端的应答后才进入ESTABLISHED状态,而服务端在收到连接请求后就进入ESTABLISHED状态。此时如果网络拥塞,客户端发送的连接请求迟迟到不了服务端,客户端便超时重发请求,如果服务端正确接收并确认应答,双方便开始通信,通信结束后释放连接。此时,如果那个失效的连接请求抵达了服务端,由于只有两次握手,服务端收到请求就会进入ESTABLISHED状态,等待发送数据或主动发送数据。但此时的客户端早已进入CLOSED状态,服务端将会一直等待下去,这样浪费服务端连接资源。
### 四次挥手
- close_wait:服务端确认关闭后,还有数据传输的需要,此时服务端的状态为`close_wait`;
## 什么是time_wait状态
* TCP 连接中,**主动关闭连接**的一方出现的状态;(收到 FIN 命令,进入 TIME\_WAIT 状态,并返回 ACK 命令)
* 保持 2 个`MSL`时间,即,`4 分钟`;(MSL 为 2 分钟)
> 基于TCP建立连接时,都会占用一个本地端口。TCP 本地端口数量,上限为`65535`(6.5w),这是因为 TCP 头部使用`16 bit`,存储「**端口号**」,因此约束上限为`65535`。
### **time_wait 状态**存在的`必要性`:
* **可靠的实现 TCP 全双工连接的终止**:四次挥手关闭 TCP 连接过程中,最后的 ACK 是由「主动关闭连接」的一端发出的,如果这个 ACK 丢失,则,对方会重发 FIN 请求,因此,在「主动关闭连接」的一段,需要维护一个 time\_wait 状态,处理对方重发的 FIN 请求;
* **处理延迟到达的报文**:由于路由器可能抖动,TCP 报文会延迟到达,为了避免「延迟到达的 TCP 报文」被误认为是「新 TCP 连接」的数据,则,需要在允许新创建 TCP 连接之前,保持一个不可用的状态,等待所有延迟报文的消失,一般设置为 2 倍的 MSL(报文的最大生存时间),解决「延迟达到的 TCP 报文」问题;
## 问题分析
> 大量的`TIME_WAIT`状态 TCP 连接存在,其本质原因是什么?
* 大量的**短连接**存在
* 特别是 HTTP 请求中,如果`connection`头部取值被设置为`close`时,基本都由「**服务端**」发起**主动关闭连接**
* 而`TCP 四次挥手`关闭连接机制中,为了保证`ACK 重发`和`丢弃延迟数据`,设置`time_wait`为 2 倍的`MSL`(报文最大存活时间)
### 大量time_wait有什么影响
Nginx 作为反向代理时,大量的短链接,可能导致 Nginx 上的 TCP 连接处于`time_wait`状态:
* 每一个 time\_wait 状态,都会占用一个「本地端口」,上限为`65535`(16 bit,2 Byte);
* 当大量的连接处于`time_wait`时,新建立 TCP 连接会出错,**address already in use : connect**异常
### 解决办法
解决上述`time_wait`状态大量存在,导致新连接创建失败的问题,一般解决办法:
**客户端**
HTTP 请求的头部,connection 设置为 keep-alive,保持存活一段时间:现在的浏览器,一般都这么进行了。
**服务器端**
* 允许`time_wait`状态的 socket 被**重用**
* 缩减`time_wait`时间,设置为`1 MSL`(即,2 mins)
## 总结
#### 1. **time\_wait 是「服务器端」的状态?or 「客户端」的状态?**
1. RE:time\_wait 是「主动关闭 TCP 连接」一方的状态,可能是「客服端」的,也可能是「服务器端」的
2. 一般情况下,都是「客户端」所处的状态;「服务器端」一般设置「不主动关闭连接」
#### 2. **服务器在对外服务时,是「客户端」发起的断开连接?还是「服务器」发起的断开连接?**
1. 正常情况下,都是「客户端」发起的断开连接
2. 「服务器」一般设置为「不主动关闭连接」,服务器通常执行「被动关闭」
3. 但 HTTP 请求中,http 头部 connection 参数,可能设置为 close,则,服务端处理完请求会主动关闭 TCP 连接
> 关于 Apache httpd 服务器的关联配置,参考:[https://elf8848.iteye.com/blog/1739571](https://elf8848.iteye.com/blog/1739571)
#### **3. 关于 HTTP 请求中,设置的主动关闭 TCP 连接的机制:TIME\_WAIT的是主动断开方才会出现的,所以主动断开方是服务端?**
1. 答案是是的。在HTTP1.1协议中,有个 Connection 头,Connection有两个值,close和keep-alive,这个头就相当于客户端告诉服务端,服务端你执行完成请求之后,是关闭连接还是保持连接,保持连接就意味着在保持连接期间,只能由客户端主动断开连接。还有一个keep-alive的头,设置的值就代表了服务端保持连接保持多久。
2. HTTP默认的Connection值为close,那么就意味着关闭请求的一方几乎都会是由服务端这边发起的。那么这个服务端产生TIME\_WAIT过多的情况就很正常了。
3. 虽然HTTP默认Connection值为close,但是,现在的浏览器发送请求的时候一般都会设置Connection为keep-alive了。所以,也有人说,现在没有必要通过调整参数来使TIME\_WAIT降低了。
> 原文阅读:https://www.cnblogs.com/soft-engineer/p/15175930.html
- 前言
- 第一部分 计算机网络与操作系统
- 大量的 TIME_WAIT 状态 TCP 连接,对业务有什么影响?怎么处理?
- 性能占用
- 第二部分 Java基础
- 2-1 JVM
- JVM整体结构
- 方法区
- JVM的生命周期
- 堆对象结构
- 垃圾回收
- 调优案例
- 类加载机制
- 执行引擎
- 类文件结构
- 2-2 多线程
- 线程状态
- 锁与阻塞
- 悲观锁与乐观锁
- 阻塞队列
- ConcurrentHashMap
- 线程池
- 线程框架
- 彻底搞懂AQS
- 2-3 Spring框架基础
- Spring注解
- Spring IoC 和 AOP 的理解
- Spring工作原理
- 2-4 集合框架
- 死磕HashMap
- 第三部分 高级编程
- Socket与NIO
- 缓冲区
- Bybuffer
- BIO、NIO、AIO
- Netty的工作原理
- Netty高性能原因
- Rabbitmq
- mq消息可靠性是怎么保障的?
- 认证授权
- 第四部分 数据存储
- 第1章 mysql篇
- MySQL主从一致性
- Mysql的数据组织方式
- Mysql性能优化
- 数据库中的乐观锁与悲观锁
- 深度分页
- 从一条SQL语句看Mysql的工作流程
- 第2章 Redis
- Redis缓存
- redis key过期策略
- 数据持久化
- 基于Redis分布式锁的实现
- Redis高可用
- 第3章 Elasticsearch
- 全文查询为什么快
- battle with mysql
- 第五部分 数据结构与算法
- 常见算法题
- 基于数组实现的一个队列
- 第六部分 真实面试案例
- 初级开发面试材料
- 答案部分
- 现场编码
- 第七部分 面试官角度
- 第八部分 计算机基础
- 第九部分 微服务
- OpenFeign工作原理