合规国际互联网加速 OSASE为企业客户提供高速稳定SD-WAN国际加速解决方案。 广告
## 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