最近项目中客户端连接服务器时死活都连不上了,服务器查询出现大量time_wait状态,于是迫在眉睫解决问题的着手点是如何避免出现大量的tima_wait状态。下面我将为大家介绍解决的方法:
下面让我们先来了温习下tcp的三次握手和四次挥手的过程图
tcp三次握手
tcp四次挥手
在TIME_WAIT状态中,如果TCP client端最后一次发送的ACK丢失了,它将重新发送。TIME_WAIT状态中所需要的时间是依赖于实现方法的。典型的值为30秒、1分钟和2分钟。等待之后连接正式关闭,并且所有的资源(包括端口号)都被释放。
1、断网关闭时先调用shutdown函数
close与shutdown的区别主要表现在:
close函数会关闭套接字ID,如果有其他的进程共享着这个套接字,那么它仍然是打开的,这个连接仍然可以用来读和写,并且有时候这是非常重要的 ,特别是对于多进程并发服务器来说。
而shutdown会切断进程共享的套接字的所有连接,不管这个套接字的引用计数是否为零,那些试图读得进程将会接收到EOF标识,那些试图写的进程将会检测到SIGPIPE信号,同时可利用shutdown的第二个参数选择断连的方式。
2、SO_REUSEPORT的使用
SO_REUSEPORT支持多个进程或者线程绑定到同一端口,提高服务器程序的性能,解决的问题:
- 允许多个套接字 bind()/listen() 同一个TCP/UDP端口
- 1)每一个线程拥有自己的服务器套接字
- 2)在服务器套接字上没有了锁的竞争
- 内核层面实现负载均衡
- 1)安全层面,监听同一个端口的套接字只能位于同一个用户下面
int opt_val = 1;
if(setsockopt(mSockFD, SOL_SOCKET, SO_REUSEPORT, &opt_val, sizeof(opt_val)))
{
cout << "set reuseport error: " << errno << endl; return -errno;
}
3、setsockopt 设置 SO_LINGER 选项
struct linger so_linger;
so_linger.l_onoff = 1;
so_linger.l_linger = 0;
setsockopt(s,
SOL_SOCKET,
SO_LINGER,
&so_linger,
sizeof so_linger);
4、设置一些超时参数
设置以下参数
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_tw_recycle = 1
net.ipv4.tcp_fin_timeout = 30