作为一个后端程序员, 网络连接这块是一个绕不过的砍, 当你在做服务器优化的时候, 网络优化也是其中一环, 那么作为网络连接中最基础的部分 - TCP 连接 你了解吗? 今天我们来仔细看看这个部分.
TCP 建立连接 - 三次握手
详解
客户端和服务器还未建立连接, 但服务器一般处于 listen 状态
客户端主动建立连接, 向服务器发送 SYN 报文, 客户端变为 SYN_SENT 状态
服务器收到客户端发送的报文, 也回了一个 SYN 报文, 包含了一个 ack. 此时, 服务器变为 SYN_RCVD 状态
客户端收到了服务器发送的 SYN 报文, 确认了 ack, 它将向服务器发送一个 ACK 报文. 此时, 客户端变为 ESTABLISHED
服务器收到客户端的 ACK 报文, 确认了 ack. 此时, 服务器也变为 ESTABLISHED
服务器和客户端可以正常通信了
其中步骤 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 断开连接 - 四次挥手
详解
客户端与服务器端正常传输数据
客户端主动断开连接, 向服务器端发送 FIN 报文, 客户端变为 FIN_WAIT1 状态
服务器收到客户端的 FIN 后, 向客户端发送 ACK 报文, 服务器变为 CLOSE_WAIT 状态
客户端收到服务器的 ACK 报文后, 客户端变为 FIN_WAIT2 状态
服务器向客户端发送 FIN 报文, 服务器变为 LAST_ACK 状态
客户端收到服务器发送的 FIN 报文后, 向服务器发送 ACK 报文, 客户端变为 TIME_WAIT 状态
服务器收到客户端的 ACK 报文后, 服务器变为 CLOSED 状态
客户端经过 2MSL(max segment lifetime, 报文最大生存时间) 时间后, 也变为 CLOSED 状态
其中, 步骤 2,3,5,6 即为 4 次挥手.
TIME_WAIT 状态及其优化
看完之后, 大家想必会有一个疑问, 为什么 TIME_WAIT 状态需要保持 2MSL? 因为这可以保证至少一次报文的往返时间内, 端口是不可复用的.
假设 TIME_WAIT 状态的持续时间很短, 我们来模拟下面这种场景:
客户端向服务器端发送了三条报文, 其中第 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 相关的配置原理, 这样应该会对我们的服务器性能调优会有更大的帮助. 有兴趣的同学不妨可以去了解一下, 如果有什么新发现想和作者探讨的, 欢迎在下方留言.
来源: http://www.tuicool.com/articles/ym6z6bf