18

TCP 连接及其优化

 4 years ago
source link: https://www.tuicool.com/articles/ym6z6bf
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

作为一个后端程序员,网络连接这块是一个绕不过的砍,当你在做服务器优化的时候,网络优化也是其中一环,那么作为网络连接中最基础的部分- TCP连接 你了解吗?今天我们来仔细看看这个部分。

TCP建立连接-三次握手

详解

b6fmQz2.jpg!web

  1. 客户端和服务器还未建立连接,但服务器一般处于  listen 状态

  2. 客户端主动建立连接,向服务器发送SYN报文,客户端变为  SYN_SENT 状态

  3. 服务器收到客户端发送的报文,也回了一个SYN报文,包含了一个ack。此时,服务器变为 SYN_RCVD 状态

  4. 客户端收到了服务器发送的SYN报文,确认了ack,它将向服务器发送一个ACK报文。此时,客户端变为 ESTABLISHED

  5. 服务器收到客户端的ACK报文,确认了ack。此时,服务器也变为 ESTABLISHED

  6. 服务器和客户端可以正常通信了

其中步骤2~4就是三次握手,那么为什么需要三次握手呢?为什么不是一次或者两次握手呢?

首先,我们需要知道,只有当服务器和客户端都能确保自己能够发消息和接收消息,这次网络通信才算成功的。

步骤2的作用是让服务器知道了自己是可以接收消息的。

步骤3的作用是让客户端知道自己发送消息和接收消息的功能是OK的,发送消息的能力是通过服务器返回的 ack=x+1 确认的,因为这个值基于当初客户端发送的消息 seq=x 。接收消息的能力是因为收到了服务器的返回。

步骤4的作用是让服务器端知道自己发送消息的能力是OK的(和步骤3类似)。

linux查看

linux服务器可以利用 netstat-anp|grep tcp 命令,查看服务器上各个端口和应用的连接状态。

你还可以通过修改linux的配置文件 /etc/sysctl.conf ,调整各个状态的数量

SYN_SENT 状态相关

  • 主动建立连接时,发SYN(步骤2)的重试次数

  • 建立连接时的本地端口可用范围

SYN_RCVD 状态相关

  • SYN_RCVD 状态连接的最大个数

  • 被动建立连接时,发SYN/ACK(步骤3)重试次数

说完了TCP建立连接,接下来,我们再来看看TCP正常断开连接的过程

TCP断开连接-四次挥手

详解

i6Vb2an.jpg!web

  1. 客户端与服务器端正常传输数据

  2. 客户端主动断开连接,向服务器端发送FIN报文,客户端变为  FIN_WAIT1 状态

  3. 服务器收到客户端的FIN后,向客户端发送ACK报文,服务器变为  CLOSE_WAIT 状态

  4. 客户端收到服务器的ACK报文后,客户端变为  FIN_WAIT2 状态

  5. 服务器向客户端发送FIN报文,服务器变为  LAST_ACK 状态

  6. 客户端收到服务器发送的FIN报文后,向服务器发送ACK报文,客户端变为  TIME_WAIT 状态

  7. 服务器收到客户端的ACK报文后,服务器变为  CLOSED 状态

  8. 客户端经过2MSL(max segment lifetime,报文最大生存时间)时间后,也变为  CLOSED 状态

其中,步骤2、3、5、6即为4次挥手。

TIME_WAIT 状态及其优化

看完之后,大家想必会有一个疑问,为什么 TIME_WAIT 状态需要保持2MSL?因为这可以保证至少一次报文的往返时间内,端口是不可复用的。

假设 TIME_WAIT 状态的持续时间很短,我们来模拟下面这种场景: Q3I7byF.jpg!web

  • 客户端向服务器端发送了三条报文,其中第3条报文卡在网络中,服务器只收到了前两条,向客户单发送ACK=2,客户端重新发送第三条报文。

  • 服务器主动发送FIN报文,客户端收到后发送FIN、ACK,服务器端收到后发送ACK并进入  TIME_WAIT 状态(假设这个状态很短)。

  • 现在服务器又再次和客户端建立连接,三次握手之后开始发送正常数据,结果之前卡住的第三条报文,现在终于发送到服务器,但服务器也不知道该如何处理这条报文。

因此这也是 TIME_WAIT 状态需要保持2MSL的原因,如果这么长时间也没有收到报文,即使有正确的报文从客户端发出,也已经过期了,因此不会影响到之后的通信。

但这同样也会带来一个问题, TIME_WAIT 状态保持的时间较长,假设服务器端有大量 TIME_WAIT 状态的TCP连接,就相当于白白浪费掉大量的服务器资源(端口)。此时,我们可以通过修改以下配置进行服务器调优:

  • 开启后,作为客户端时新连接可以使用仍然处于  TIME_WAIT 状态的端口

  • 由于timestamp的存在,操作系统可以拒绝迟到的报文(例如上面说的第三条报文),可以利用以下配置:

其他状态的优化

CLOSE_WAIT 状态

如果服务器端有大量 CLOSE_WAIT 状态的连接,很有可能是应用进程出现bug,没有及时关闭连接。

FIN_WAIT1 状态

调整发送FIN报文的重试次数,0相当于8

FIN_WAIT2 状态

调整保持在 FIN_WAIT2 状态的时间

总结

看到这里,想必你应该对TCP连接有了一个大致的了解。现在服务器大多都用了nginx做了负载均衡,因此,我们可能需要在此基础上了解一些nginx相关的配置原理,这样应该会对我们的服务器性能调优会有更大的帮助。有兴趣的同学不妨可以去了解一下,如果有什么新发现想和作者探讨的,欢迎在下方留言。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK