A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
首先 Client 端发送连接请求报文,Server 端接受连接后回复 ACK
报文,并为这次连接分配资源。Client 端接收到 ACK
报文后也向 Server 段发生 ACK
报文,并分配资源,这样 TCP 连接就建立了。
然后是中间部分: 两者之间可以传输数据了
再次,下面的断开链接:【注意】中断连接端可以是 Client 端,也可以是 Server 端。
假设 Client 端发起中断连接请求,也就是发送 FIN
报文。Server 端接到 FIN
报文后,意思是说:“我 Client 端没有数据要发给你了”,但是如果你还有数据没有发送完成,则不必急着关闭 Socket,可以继续发送数据。
所以你先发送 ACK
,“告诉 Client 端,你的请求我收到了,但是我还没准备好,请继续你等我的消息”。这个时候 Client 端就进入 FIN_WAIT
状态,继续等待 Server 端的 FIN
报文。当 Server 端确定数据已发送完成,则向 Client 端发送 FIN
报文,“告诉 Client端,好了,我这边数据发完了,准备好关闭连接了”。
Client 端收到 FIN
报文后,就知道可以关闭连接了,但是他还是不相信网络,怕 Server 端不知道要关闭,所以发送 ACK
后进入 TIME_WAIT 状态,如果 Server 端没有收到 ACK
则可以重传。Server 端收到 ACK
后,“就知道可以断开连接了”。Client 端等待了 2MSL 后依然没有收到回复,则证明 Server 端已正常关闭,那好,我 Client 端也可以关闭连接了。
OK,TCP连接就这样关闭了!
那么可以这么理解,当 Client 进入 TIME_WAIT 的等待时间是 2 个 MSL(Maximum Segment Lifetime - 报文最大生存时间)。
让我们看一下一台 Linux 服务器的网络状态:
$ netstat -an | awk '/^tcp/ {++State[$NF]}END{for(key in State)print key "\t" State[key]}'LAST_ACK 7 LISTEN 9 SYN_RECV 2 CLOSE_WAIT 125 ESTABLISHED 1070 FIN_WAIT1 17 FIN_WAIT2 247 CLOSING 4 TIME_WAIT 25087
对于网站来说,这样的 TIME_WAIT 略显偏高, 也就是说大量的关闭操作在等待 2 个 MSL 后结束,正常我们的 TCP 端口是 65535
个,如果并发再高一些,可能会大量的 Socket 不能及时被释放,从而导致性能下降,所以我们可以通过 Linux 内核进行一些网络调整比如,开启 Socket 重用和快速回收,文件 /etc/sysctl.conf
,生效命令为 sysctl -p
:
net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_max_tw_buckets = 5000 net.ipv4.tcp_max_syn_backlog = 8192 net.ipv4.tcp_keepalive_time = 1200 net.ipv4.ip_local_port_range = 1024 65000
net.ipv4.tcp_syncookies = 1
表示开启 SYN Cookies。当出现 SYN 等待队列溢出时,启用 cookies 来处理,可防范少量 SYN 攻击,默认为 0,表示关闭;
net.ipv4.tcp_tw_reuse = 1
表示开启重用。允许将 TIME-WAIT Sockets 重新用于新的TCP连接,默认为 0,表示关闭;
net.ipv4.tcp_tw_recycle = 1
表示开启TCP连接中 TIME-WAIT Sockets 的快速回收,默认为 0,表示关闭。
系统 tcp_timestamps
缺省就是开启的,所以当 tcp_tw_recycle
被开启后,实际上这种行为就被激活了.如果服务器身处 NAT 环境,安全起见,通常要禁止 tcp_tw_recycle
,至于 TIME_WAIT 连接过多的问题,可以通过激活 tcp_tw_reuse 来缓解。
net.ipv4.tcp_max_tw_buckets = 5000
表示系统同时保持TIME_WAIT套接字的最大数量,如果超过这个数字,TIME_WAIT 套接字将立刻被清除并打印警告信息。默认为 180000,改为 5000。对于 Apache、Nginx 等服务器,上几行的参数可以很好地减少 TIME_WAIT 套接字数量,但是对于 Squid,效果却不大。此项参数可以控制 TIME_WAIT 套接字的最大数量,避免 Squid 服务器被大量的 TIME_WAIT 套接字拖死。
net.ipv4.tcp_max_syn_backlog = 8192
表示 SYN 队列的长度,默认为 1024,加大队列长度为 8192,可以容纳更多等待连接的网络连接数。
net.ipv4.tcp_keepalive_time = 1200
表示当 keepalive 起用的时候,TCP 发送 keepalive 消息的频度。缺省是 2 小时,改为 20 分钟。
net.ipv4.ip_local_port_range = 1024-65000
表示用于向外连接的端口范围。缺省情况下很小:32768 到 61000,改为 1024 到 65000。
$ netstat -an | awk '/^tcp/ {++State[$NF]}END{for(key in State)print key "\t" State[key]}' LAST_ACK 140 LISTEN 9 SYN_RECV 7 CLOSE_WAIT 2 ESTABLISHED 972 FIN_WAIT1 21 FIN_WAIT2 152 CLOSING 2 TIME_WAIT 682
net.ipv4.ip_forward = 1 fs.file-max=1000000 net.ipv4.tcp_sack = 1 net.ipv4.tcp_window_scaling = 1 net.ipv4.tcp_rmem = 4096 87380 4194304 net.ipv4.tcp_wmem = 4096 16384 4194304 net.core.wmem_default = 8388608 net.core.rmem_default = 8388608 net.core.rmem_max = 16777216 net.core.wmem_max = 16777216 net.ipv4.tcp_fin_timeout = 20 net.ipv4.tcp_syn_retries = 2 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_mem = 94500000 915000000 927000000 net.ipv4.tcp_max_orphans = 3276800 vm.swappiness = 0 net.ipv4.neigh.default.gc_stale_time=120 net.ipv4.conf.all.rp_filter=0 net.ipv4.conf.default.rp_filter=0 net.ipv4.conf.default.arp_announce = 2 net.ipv4.conf.all.arp_announce=2 net.ipv4.tcp_max_tw_buckets = 5000 net.ipv4.tcp_syncookies = 1 net.ipv4.tcp_max_syn_backlog = 40240 net.core.netdev_max_backlog = 40240 net.core.somaxconn = 40240 net.ipv4.tcp_synack_retries = 2 net.ipv4.conf.lo.arp_announce=2 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_tw_recycle = 0 <---- 不要开启tcp_tw_recycle,尤其是 NAT 下 net.ipv4.ip_local_port_range = 1024 65000
sysctl -p