20190604(TCP KeepAlive,以及HTTP Keep-Alive了解)

目录

1.TCP keepAlive的机理及使用

2.setsockopt()对keepAlive的设置

3.HTTP Keep-Alive


1.TCP keepAlive的机理及使用

TCP 是面向连接的 , 在实际应用中通常都需要检测对端是否还处于连接中。如果已断开连接,主要分为以下几种情况:

1.连接的对端正常关闭,即使用 closesocket 关闭连接。   

2.连接的对端非正常关闭,包括对端异常关闭,网络断开等情况。

针对对于第二种情况,可能会要麻烦一些。在网上找到了一些文章,大致有以下两种解决方法:

         1).自己编写心跳包程序

简单的说也就是在自己的程序中加入一条线程,定时向对端发送数据包,查看是否有 ACK ,如果有则连接正常,没有的话则连接断开。

2).使用 TCP 的 keepalive 机制

首先说一下 keepalive 来判断异常断开的原理,其实 keepalive 的原理就是 TCP 内嵌的一个心跳包。

以服务器端为例,如果当前 server 端检测到超过一定时间(默认是 7,200,000 milliseconds ,也就是 2 个小时)没有数据传输,那么会 向client 端发送一个 keep-alive packet (该 keep-alive packet 就是 ACK 和当前 TCP 序列号减一的组合),此时 client 端应该为以下三种情况之一:

1. client 端仍然存在,网络连接状况良好。此时 client 端会返回一个 ACK 。 server 端接收到 ACK 后重置计时器,在2 小时后再发送探测。如果 2 小时内连接上有数据传输,那么在该时间基础上向后推延 2 个小时。

2. 客户端异常关闭,或是网络断开,或者正在重启过程中。在这三种情况下, client 端都不会响应。服务器没有收到对其发出探测的响应,并且在一定时间(系统默认为 1000 ms )后重复发送 keep-alive packet ,并且重复发送一定次数( 2000 XP 2003 系统默认为 5 次 , Vista 后的系统默认为 10 次)。

3. 客户端曾经崩溃,但已经重启。这种情况下,服务器将会收到对其存活探测的响应,但该响应是一个复位,从而引起服务器对连接的终止。

4.客户端主机活跃运行,但从服务器不可到达。这与第2条类似,因为TCP无法区别它们两个。它所能表明的仅是未收到对其探测的回复。

开启Keepalive功能需要消耗额外的宽带和流量,尽管这微不足道,但在按流量计费的环境下增加了费用,另一方面,Keepalive设置不合理时可能会因为短暂的网络波动而断开健康的TCP连接。

服务器不必担心客户端主机被关闭然后重启的情况(这里指的是操作员执行的正常关闭,而不是主机的崩溃)。当系统被操作员关闭时,所有的应用程序进程(也就是客户端进程)都将被终止,客户端TCP会在连接上发送一个FIN。收到这个FIN后,服务器TCP向服务器进程报告一个文件结束,以允许服务器检测这种状态。

在第一种状态下,服务器应用程序不知道存活探测是否发生。凡事都是由TCP层处理的,存活探测对应用程序透明,直到后面2,3,4三种状态发生。在这三种状态下,通过服务器的TCP,返回给服务器应用程序错误信息。(通常服务器向网络发出一个读请求,等待客户端的数据。如果存活特征返回一个错误信息,则将该信息作为读操作的返回值返回给服务器。)

在状态2,错误信息类似于“连接超时”。

状态3则为“连接被对方复位”。

状态4看起来像连接超时,或者根据是否收到与该连接相关的ICMP错误信息,而可能返回其它的错误信息。


2.setsockopt()对keepAlive的设置

1.SO_KEEPALIVE--------->开启keep-alive;默认下是关闭的,在TCP的socket中单独开启

在《UNIX网络编程第1卷》中也有详细的阐述:

SO_KEEPALIVE 保持连接检测对方主机是否崩溃,避免(服务器)永远阻塞于TCP连接的输入。设置该选项后,如果2小时内在此套接口的任一方向都没有数据交换,TCP就自 动给对方 发一个保持存活探测分节(keepalive probe)。这是一个对方必须响应的TCP分节.它会导致以下三种情况:

对方接收一切正常:以期望的ACK响应。2小时后,TCP将发出另一个探测分 节。

对方已崩溃且已重新启动:以RST响应。套接口的待处理错误被置为ECONNRESET,套接 口本身则被关闭。

对方无任何响应:源自berkeley的TCP发送另外8个探测分节,相隔75秒一个,试图得到一个响应。在发出第一个探测分节11分钟 15秒后若仍无响应就放弃。套接口的待处理错误被置为ETIMEOUT,套接口本身则被关闭。

如ICMP错误是“host unreachable(主机不可达)”,说明对方主机并没有崩溃,但是不可达,这种情况下待处理错误被置为 EHOSTUNREACH。

根据上面的介绍我们可以知道对端以一种非优雅的方式断开连接的时候,我们可以设置SO_KEEPALIVE属性使得我们在2小时以后发现对方的TCP连接是否依然存在。   

int keepAlive = 1;

Setsockopt(listenfd, SOL_SOCKET, SO_KEEPALIVE, (void*)&keepAlive, sizeof(keepAlive));

2.如果我们不能接受如此之长的等待时间,从TCP-Keepalive-HOWTO上可以知道一共有两种方式可以设置

  (1)一种--------->修改内核关于网络方面的 配置参数,即:进入内核的.h中设置

     PS:KeepAlive默认不是开启的,如果想使用KeepAlive,需要在你的应用中设置SO_KEEPALIVE才可以生效。

     查看当前的配置:

cat /proc/sys/net/ipv4/tcp_keepalive_time

cat /proc/sys/net/ipv4/tcp_keepalive_intvl

cat /proc/sys/net/ipv4/tcp_keepalive_probes

    在Linux中我们可以通过修改 /etc/sysctl.conf 的全局配置:

net.ipv4.tcp_keepalive_time=7200

net.ipv4.tcp_keepalive_intvl=75

net.ipv4.tcp_keepalive_probes=9

      如果应用中已经设置SO_KEEPALIVE,程序不用重启,修改上面的配置后输入 sysctl -p 使其生效;

      可以使用 sysctl -a | grep keepalive 命令来查看当前的默认配置

 (2)一种---------->就是SOL_TCP字段的TCP_KEEPIDLETCP_KEEPINTVLTCP_KEEPCNT三个选项。

TCP_KEEPIDLE 开启keepalive的闲置时 长
TCP_KEEPINTVL keepalive探测包的发送间隔
TCP_KEEPCNT 如果对方不予应答,探测包的发送次数
int  keepIdle = 1000;

int  keepInterval = 10;

int  keepCount = 10;

Setsockopt(listenfd, SOL_TCP, TCP_KEEPIDLE, (void *)&keepIdle, sizeof(keepIdle));

Setsockopt(listenfd, SOL_TCP,TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));

Setsockopt(listenfd,SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

 

备注:

1.长连接虽好,但是比较好用但是占用系统资源比较大。个人建议如无特殊需要,用自己的心跳包机制最好

2.请记住,保持连接与程序无关,而与套接字相关,因此,如果您有多个套接字,则可以分别为每个套接字处理保持连接。


3.HTTP Keep-Alive

 HTTP层的Keep-Alive是什么概念呢?TCP在建立链接之后, HTTP协议使用TCP传输HTTP协议的请求(Request)和响应(Response)数据,一次完整的HTTP事务如下图:

这张图我简化了HTTP(Req)和HTTP(Resp),实际上的请求和响应需要多个TCP报文。 

下面三段每句话都好好读,都是重点,没有废话

从图中可以发现一个完整的HTTP事务,有链接的建立请求的发送响应接收断开链接这四个过程;早期通过HTTP协议传输的数据以文本为主,一个请求可能就把所有要返回的数据取到,但是,现在要展现一张完整的页面需要很多个请求才能完成,如图片,JS,CSS等,如果每一个HTTP请求都需要新建并断开一个TCP,这个开销是完全没有必要的。

开启HTTP Keep-Alive之后,能复用已有的TCP链接,当前一个请求已经响应完毕,服务器端没有立即关闭TCP链接,而是等待一段时间接收浏览器端可能发送过来的第二个请求,通常浏览器在第一个请求返回之后会立即发送第二个请求,如果某一时刻只能有一个链接,同一个TCP链接处理的请求越多,开启KeepAlive能节省的TCP建立和关闭的消耗就越多。

当然通常会启用多个链接去从服务器器上请求资源,但是开启了Keep-Alive之后,仍然能加快资源的加载速度。HTTP/1.1之后默认开启Keep-Alive, 在HTTP的头域中增加Connection选项。当设置为Connection:keep-alive表示开启,设置为Connection:close表示关闭。实际上HTTP的KeepAlive写法是Keep-Alive,跟TCP的KeepAlive写法上也有不同。所以TCP KeepAlive和HTTP的Keep-Alive不是同一回事情。

你可能感兴趣的:(学习,网络,TCP,KeepAlive,HTTP,Keep-Alive)