在用户进程启用了保活定时器的情况下,如果连接超过空闲时间没有数据交互,则保活定时器超时,向对端发送保活探测包,若(1)收到回复则说明对端工作正常,重置定时器等下下次达到空闲时间;(2) 收到其他回复,则确定对端已重启,关闭连接;(3) 超过探测次数仍未得到回复,则认为对端主机已经崩溃,关闭连接;

启动定时器:

用户进程可以通过socket的SO_KEEPALIVE选项来开启或关闭保活定时器探测,TCP最终会调用tcp_set_keepalive来实现保活定期的开启与关闭;

 int sock_setsockopt(struct socket *sock, int level, int optname,
char __user *optval, unsigned int optlen)
{
struct sock *sk = sock->sk;
int val;
int valbool;
struct linger ling;
int ret = ; valbool = val ? : ; lock_sock(sk); switch (optname) {
case SO_KEEPALIVE:
if (sk->sk_prot->keepalive)
sk->sk_prot->keepalive(sk, valbool);
sock_valbool_flag(sk, SOCK_KEEPOPEN, valbool);
break;
}
}
 struct proto tcp_prot = {
.name = "TCP",
/* 省略部分字段 */
.keepalive = tcp_set_keepalive,
/* 省略部分字段 */
}
 void tcp_set_keepalive(struct sock *sk, int val)
{
if (( << sk->sk_state) & (TCPF_CLOSE | TCPF_LISTEN))
return; if (val && !sock_flag(sk, SOCK_KEEPOPEN))
inet_csk_reset_keepalive_timer(sk, keepalive_time_when(tcp_sk(sk)));
else if (!val)
inet_csk_delete_keepalive_timer(sk);
}

定时器回调函数:

tcp_keepalive_timer函数为保活定时器和FIN_WAIT_2定时器共用,我们这里只关注保活部分;函数执行必要的状态检查,之后对空闲时间和配置空闲时间阈值进行判断,在超过阈值的情况下,若未超过探测次数和用户配置超时时间,则发送探测包,否则关闭连接;

 static void tcp_keepalive_timer (unsigned long data)
{
struct sock *sk = (struct sock *) data;
struct inet_connection_sock *icsk = inet_csk(sk);
struct tcp_sock *tp = tcp_sk(sk);
u32 elapsed; /* Only process if socket is not in use. */
bh_lock_sock(sk); /* 传输控制块被用户进程锁定 */
if (sock_owned_by_user(sk)) {
/* Try again later. */
/* 重置定时器 */
inet_csk_reset_keepalive_timer (sk, HZ/);
goto out;
} /* 连接处于LISTEN状态,退出 */
if (sk->sk_state == TCP_LISTEN) {
pr_err("Hmm... keepalive on a LISTEN ???\n");
goto out;
} /* 处于fin_wait2且socket即将销毁,用作FIN_WAIT_2定时器 */
if (sk->sk_state == TCP_FIN_WAIT2 && sock_flag(sk, SOCK_DEAD)) { /* 停留在FIN_WAIT_2的停留时间>=0 */
if (tp->linger2 >= ) {
/* 获取在FIN_WAIT_2的剩余时间 */
const int tmo = tcp_fin_time(sk) - TCP_TIMEWAIT_LEN; /* 有剩余时间则调用FIN_WAIT_2定时器 */
if (tmo > ) {
tcp_time_wait(sk, TCP_FIN_WAIT2, tmo);
goto out;
}
} /* 发送rst */
tcp_send_active_reset(sk, GFP_ATOMIC);
goto death;
} /* 未启用保活|| 状态处于关闭或者发送syn状态,退出 */
if (!sock_flag(sk, SOCK_KEEPOPEN) ||
(( << sk->sk_state) & (TCPF_CLOSE | TCPF_SYN_SENT)))
goto out; /* 获取设定的连接空闲时间 */
elapsed = keepalive_time_when(tp); /* It is alive without keepalive 8) */
/* 有发送未确认的包或者还有待发送的包,不是空闲状态 */
if (tp->packets_out || tcp_send_head(sk))
goto resched; /* 从上次收到包到现在的空闲时间 */
elapsed = keepalive_time_elapsed(tp); /* 连接空闲时间超过设定值 */
if (elapsed >= keepalive_time_when(tp)) {
/* If the TCP_USER_TIMEOUT option is enabled, use that
* to determine when to timeout instead.
*/
/*
设置了用户超时,空闲时间达到用户超时时间,已发送过探测
未设置用户超时,探测次数达到了保活最大探测次数
则发送rst关闭连接
*/
if ((icsk->icsk_user_timeout != &&
elapsed >= icsk->icsk_user_timeout &&
icsk->icsk_probes_out > ) ||
(icsk->icsk_user_timeout == &&
icsk->icsk_probes_out >= keepalive_probes(tp))) {
/* 发送rst */
tcp_send_active_reset(sk, GFP_ATOMIC); /* 关闭连接 */
tcp_write_err(sk);
goto out;
} /* 发送保活探测包 */
if (tcp_write_wakeup(sk, LINUX_MIB_TCPKEEPALIVE) <= ) {
/* 探测次数增加 */
icsk->icsk_probes_out++;
/* 下一次探测时间 */
elapsed = keepalive_intvl_when(tp);
} else {
/* If keepalive was lost due to local congestion,
* try harder.
*/
/* 本地拥塞导致的失败,则重置定时器 */
elapsed = TCP_RESOURCE_PROBE_INTERVAL;
}
} else {
/* It is tp->rcv_tstamp + keepalive_time_when(tp) */
/* 未超过空闲时间,则计算将要达到空闲的时间 */
elapsed = keepalive_time_when(tp) - elapsed;
} sk_mem_reclaim(sk); resched:
/* 重置定时器 */
inet_csk_reset_keepalive_timer (sk, elapsed);
goto out; death:
tcp_done(sk); out:
bh_unlock_sock(sk);
sock_put(sk);
}

TCP定时器 之 保活定时器的更多相关文章

  1. 14.TCP的坚持定时器和保活定时器

    一.坚持定时器   1.坚持定时器的由来         TCP通过让接收方指明希望从发送方接受的窗口大小来进行流量控制.设置窗口大小为0可以组织发送方传送数据,直至窗口变为非0为止.         ...

  2. TCP的定时器系列 — 保活定时器

    主要内容:保活定时器的实现,TCP_USER_TIMEOUT选项的实现. 内核版本:3.15.2 我的博客:http://blog.csdn.net/zhangskd 原理 HTTP有Keepaliv ...

  3. 【TCP/IP详解 卷一:协议】第二十三章 TCP的保活定时器

    本章介绍保活定时器. 在 TCP 的三握四挥 章节中,我们介绍了 处在 TIME_WAIT 的 2MSL定时器:在 TCP的超时与重传 章节中,我们介绍了 重传定时器:在上一章节中,我们介绍了 防止死 ...

  4. 动手学习TCP:4种定时器

    上一篇中介绍了TCP数据传输中涉及的一些基本知识点.本文让我们看看TCP中的4种定时器. TCP定时器 对于每个TCP连接,TCP管理4个不同的定时器,下面看看对4种定时器的简单介绍. 重传定时器使用 ...

  5. 【网络协议】TCP中的四大定时器

    前言 对于每个TCP连接,TCP一般要管理4个不同的定时器:重传定时器.坚持定时器.保活定时器.2MSL定时器. 重传定时器 非常明显重传定时器是用来计算TCP报文段的超时重传时间的(至于超时重传时间 ...

  6. TCP/IP详解学习笔记(13)-TCP坚持定时器,TCP保活定时器

    TCP一共有四个主要的定时器,前面已经讲到了一个--超时定时器--是TCP里面最复杂的一个,另外的三个是: 坚持定时器 保活定时器 2MSL定时器 其中坚持定时器用于防止通告窗口为0以后双方互相等待死 ...

  7. 《TCP/IP详细解释》札记(23章)-TCP该保活定时器

    可能有这样的备用现实TCP连接:流通过. 也就是说.假设TCP连接的两方都没有向对方发送数据.则在两个TCP模块之间不交换不论什么信息,这意味着我们能够启动一个客户与server建立连接,然后长时间不 ...

  8. TCP 的保活定时器

    引言 可以没有任何数据流过一个空闲的 TCP 连接. 这意味着我们可以启动一个客户与服务器建 立一个连接,然后离去数小时.数天.数个星期或者数月,而连接依然保持.中间路由器可以崩溃和重启,电话线可以被 ...

  9. TCP的保活定时器 转

    http://blog.csdn.net/zhangskd/article/details/44177475 TCP的Keepalive,目的在于看看对方有没有发生异常,如果有异常就及时关闭连接. 当 ...

随机推荐

  1. IdentitiServser4 + Mysql实现Authorization Server

    Identity Server 4官方文档:https://identityserver4.readthedocs.io/en/latest/ 新建2个asp.net core 项目使用空模板 Aut ...

  2. 设置adb shell的环境变量

    1.设置adb系统变量 adb  D:\androidStudio\platform-tools;D:\androidStudio\tools 2.设置path系统变量 path D:\android ...

  3. jQuery效果--淡入和淡出

    jQuery Fading 方法 通过 jQuery,您可以实现元素的淡入淡出效果. jQuery 拥有下面四种 fade 方法: fadeIn() fadeOut() fadeToggle() fa ...

  4. spring cloud 入门

    某种程度上 软硬件 殊途同归了 (软件模仿硬件 总线设计, 资源定位 (寻址) ) spring 是什么 EDA ( Event-driven architecture ) (SOA , SOAP , ...

  5. idea安装完成后要做的几件事(设置字体、编码、行号)

    1.设置字体大小和样式 打开设置:File-->Settings 看到如下界面,输入font,点击Editor目录下的Font设置字体大小和样式: Font:字体样式 size:字体大小 Fal ...

  6. 从FBV到CBV三(权限)

    丛FBC到CBV三(权限) span::selection, .CodeMirror-line > span > span::selection { background: #d7d4f0 ...

  7. CISCO运维记录之4507设备升级IOS(Version 03.03.02.SG版本存在bug)

    CISCO运维记录之3650堆叠设备升级IOS(Version 03.03.02.SG版本存在bug) 1. 问题详情 思科45系列交换机使用Catalyst 4500 L3 Switch Softw ...

  8. Summer training round2 #4 (Training #20)

    A!:UESTC1752 B!:找区间内L到R之间内的数的个数  权值分块加莫队 C!:给你一个哈斯图 去掉其中的几条边 要求输出字典序最大的拓扑排序:线段树模拟拓扑排序 D!:要求你找到最短路树并输 ...

  9. mongodb 3.0 WT 引擎性能测试(转载)

    网上转载来的测试,仅供参考.原文地址:http://www.mongoing.com/benchmark_3_0 类机器. 测试均在单机器,单实例的情况下进行. 机器A(cache 12G,即内存&g ...

  10. https://github.com/zabbix/zabbix-docker 安装

    docker-compose -f ./docker-compose_v3_centos_mysql_latest.yaml up -d 解压文件,运行即可