长连接与短连接
- 使用TCP协议时,会在客户端和服务器之间建立一条虚拟的信道,这条虚拟信道就是指连接,而建议这条连接需要3次握手,拆毁这条连接需要4次挥手,可见,我们建立这条连接是有成本的,这个成本就是效率成本,简单点说就是时间成本,你要想发送一段数据,必须先3次握手(来往3个包),然后才能发送数据,发送完了,你需要4次挥手(来往4个包)来断开这个连接。
- 其二,CPU资源成本,三次握手和4次挥手和发送数据都是从网卡里发送出去和接收的,还有其余的设备,比如防火墙,路由器等等,站在操作系统内核的角度来讲,如果我们是一个高并发系统的话,如果大量的数据包都经历过这么一个过程,那是很耗CPU的。
- 其三,每个socket是需要耗费系统缓存的,比如系统提供了一些接口设置socket缓存的,比如:
/proc/sys/net/ipv4/tcp_rmem
/proc/sys/net/ipv4/tcp_wmem
/proc/sys/net/ipv4/tcp_mem
keepalive机制
TCP连接一旦建立后,是不是这个连接可以一直保持? 答案是否定的,操作系统在实现TCP协议的时候都做了一个限制,这个限制可以参考配置:
cat /proc/sys/net/ipv4/tcp_keepalive_time
cat /proc/sys/net/ipv4/tcp_keepalive_intvl
cat /proc/sys/net/ipv4/tcp_keepalive_probes
- 我们看到默认这个tcp_keepalive_time的值为7200s,也就是2个小时,这个值代表如果TCP连接发送完最后一个ACK包后,如果超过2个小时,没有数据往来,那么这个连接会断掉。那么我们如何才能保持住这个连接呢?实际上,这就是TCP的keepalive机制,哦,说法不严谨,TCP协议并没有规定如此,但是很多的操作系统内核实现TCP协议时,都加上了这个keepalive机制,那么这个功能默认是关闭的,那这个keepalive机制到底是如何的呢?也就是,如果TCP之间没有任何数据来往了在tcp_keepalive_time(7200s,2h)后,服务器给客户端发送一个探测包,如果对方有回应,说明这个连接还存活,否则继续每隔tcp_keepalive_intvl(默认为75s)给对方发送探测包,如果连续tcp_keepalive_probes(默认为9)次后,依然没有收到对端的回复,那么则认为这个连接已经关闭。
- tcp keepalive默认不是开启的,如果想使用KeepAlive,需要在你的应用中设置SO_KEEPALIVE才可以生效。当然事先我们可以修改这些默认值,方法如下:
在Linux中我们可以通过修改 /etc/sysctl.conf 的全局配置:
net.ipv4.tcp_keepalive_time=7200
net.ipv4.tcp_keepalive_intvl=75
net.ipv4.tcp_keepalive_probes=9
添加上面的配置后输入 sysctl -p 使其生效,你可以使用 sysctl -a | grep keepalive 命令来查看当前的默认配置
tcp keepalive可以通过设置TCP选项设置,设置方法如下:
#include <sys/socket.h>
int setsockopt(int socket, int level, int option_name, const void *option_value, socklen_t option_len);
我们在需要使能Keepalive的socket上面调用setsockopt函数便可以打开该socket上面的keepalive。
第一个参数是要设置的套接字 第二个参数是SOL_SOCKET 第三个参数必须是SO_KEEPALIVE 第四个参数必须是一个布尔整型值,0表示关闭,1表示打开 最后一个参数是第四个参数值的大小。
示例:
int keepalive = 1;
setsockopt(incomingsock,SOL_SOCKET,SO_KEEPALIVE,(void*)(&keepalive),(socklen_t)sizeof(keepalive));
int keepalive_time = 30;
setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPIDLE,(void*)(&keepalive_time),(socklen_t)sizeof(keepalive_time));
int keepalive_intvl = 3;
setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPINTVL,(void*)(&keepalive_intvl),(socklen_t)sizeof(keepalive_intvl));
int keepalive_probes= 3;
setsockopt(incomingsock, IPPROTO_TCP, TCP_KEEPCNT,(void*)(&keepalive_probes),(socklen_t)sizeof(keepalive_probes));
设置SO_KEEPALIVE选项来开启KEEPALIVE,然后通过TCP_KEEPIDLE、TCP_KEEPINTVL和TCP_KEEPCNT设置keepalive的保活时间、间隔、次数等参数。