Linux下关于TCP的keep alive的实现源码分析
来源:互联网 发布:捕鱼游戏编程原理 编辑:程序博客网 时间:2024/05/21 06:00
TCP下的Keep Alive
我们常说的TCP的keep alive,就是为了保证连接的有效性,在间隔一定的时间发探测包,根据回复来确认该连接是否有效。通常上层应用会自己提供心跳检测机制,而Linux内核本身也提供了从内核层面的确保连接有效性的方式。
在sock 函数中可以设置是否需要打开keep alive开关,默认建立socket 是关闭keep alive的。代码如下
optval = 1; optlen = sizeof(optval); if(setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { perror("setsockopt()"); close(s); exit(EXIT_FAILURE); }
Keep Alive 的控制参数
tcp_keepalive_time 参数
控制keep alive的最长空闲时间tcp_keepalive_probes 参数
当超过最长空间时间后,内核会尝试发出探测包确认客户端时候存活,该参数控制的是尝试的次数
tcp_keepalive_intvl 参数
当超过最长空闲时间后,内核会发出探测包,当没有收到确认回复的,该参数控制下个探测包的时间Linux下如何实现Keep Alive
sock结构体中的timer_list
在sock结构体中,存在timer_list的结构体sk_timer,参考下面结构
struct sock{...struct timer_listsk_timer;...}
struct timer_list {struct list_head entry;unsigned long expires;void (*function)(unsigned long);unsigned long data;struct tvec_base *base;#ifdef CONFIG_TIMER_STATSvoid *start_site;char start_comm[16];int start_pid;#endif#ifdef CONFIG_LOCKDEPstruct lockdep_map lockdep_map;#endif};timer_list结构体,是在sock里常用的timer执行链表,entry代表的是链表的头, expires代表的失效时间,而function就是执行的函数。
注册keepalive处理函数
void inet_csk_init_xmit_timers(struct sock *sk, void (*retransmit_handler)(unsigned long), void (*delack_handler)(unsigned long), void (*keepalive_handler)(unsigned long)){struct inet_connection_sock *icsk = inet_csk(sk);setup_timer(&icsk->icsk_retransmit_timer, retransmit_handler,(unsigned long)sk);setup_timer(&icsk->icsk_delack_timer, delack_handler,(unsigned long)sk);setup_timer(&sk->sk_timer, keepalive_handler, (unsigned long)sk);//注册了函数在sk_timer中icsk->icsk_pending = icsk->icsk_ack.pending = 0;}
当连接完成的时候(也就是握手成功的时候),在新生成的sock里面的sk_timer结构体中,注册了函数keepalive_handler函数
void tcp_init_xmit_timers(struct sock *sk){inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer, &tcp_keepalive_timer);}
而keepalive_handler函数就是tcp_keepalive_timer函数
static void tcp_keepalive_timer (unsigned long data){......if (!sock_flag(sk, SOCK_KEEPOPEN) || sk->sk_state == TCP_CLOSE)goto out;elapsed = keepalive_time_when(tp);/* It is alive without keepalive 8) */if (tp->packets_out || tcp_send_head(sk))goto resched;elapsed = tcp_time_stamp - tp->rcv_tstamp;if (elapsed >= keepalive_time_when(tp)) {if (icsk->icsk_probes_out >= keepalive_probes(tp)) {tcp_send_active_reset(sk, GFP_ATOMIC);tcp_write_err(sk);goto out;}if (tcp_write_wakeup(sk) <= 0) {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;} .....}
上面只是截取了一部分代码,重点是前面提到的参数的实现,代码首先先检查了是否在sock里设置了参数SO_KEEPALIVE,也就是sock里面的flag:SOCK_KEEPOPEN。
如果设置了socket的SO_KEEPALIVE,才继续检查时间戳,取的上次收到包的时间戳和当前时间戳的差值,进行和参数keepalive_time的比较,如果已经超时了,那么检查发已经出探测包失败的次数,如果次数已经比keepalive_probes的大,那么发出reset包,同时写错误报告,关闭sock。
如果比设置的探测包次数小的话,那发出探测包,同时设置下次的校验的时间戳为keepalive_intvl, 而不在是keepalive_time。
注意:在这里keepalive_intvl只是控制触发下次校验的时间
计算结束无效连接的时间N会有两种情况
a. keepalive_intvl 的时间比 keepalive_time 大
N=keepalive_time +keepalive_intvl*keepalive_probes
b. keepalive_intvl 的时间比 keepalive_time小
N=keepalive_time +keepalive_time*keepalive_probes
这也就是为什么在默认设置里,认为无效的连接的时间实际上是7200*6 要12小时才会断掉连接
代码中设置keepalive_time,keepalive_probes,keepalive_intvl
setsockopt(s, SOL_TCP, TCP_KEEPIDLE, &val, sizeof(int)) setsockopt(s, SOL_TCP, TCP_KEEPINTVL, &val, sizeof(int)) setsockopt(s, SOL_TCP, TCP_KEEPCNT, &val, sizeof(int))
所对应的3个参数 TCP_KEEPIDLE --> keepalive_time, TCP_KEEPINTVL--> keepalive_intvl, TCP_KEEPCNT--> keepalive_probes
处理中的定时器
JAVA设置Keep alive
const opts[] = { { java_net_SocketOptions_TCP_NODELAY, IPPROTO_TCP, TCP_NODELAY }, { java_net_SocketOptions_SO_OOBINLINE, SOL_SOCKET, SO_OOBINLINE }, { java_net_SocketOptions_SO_LINGER, SOL_SOCKET, SO_LINGER }, { java_net_SocketOptions_SO_SNDBUF, SOL_SOCKET, SO_SNDBUF }, { java_net_SocketOptions_SO_RCVBUF, SOL_SOCKET, SO_RCVBUF }, { java_net_SocketOptions_SO_KEEPALIVE, SOL_SOCKET, SO_KEEPALIVE }, { java_net_SocketOptions_SO_REUSEADDR, SOL_SOCKET, SO_REUSEADDR }, { java_net_SocketOptions_SO_BROADCAST, SOL_SOCKET, SO_BROADCAST }, { java_net_SocketOptions_IP_TOS, IPPROTO_IP, IP_TOS }, { java_net_SocketOptions_IP_MULTICAST_IF, IPPROTO_IP, IP_MULTICAST_IF }, { java_net_SocketOptions_IP_MULTICAST_IF2, IPPROTO_IP, IP_MULTICAST_IF }, { java_net_SocketOptions_IP_MULTICAST_LOOP, IPPROTO_IP, IP_MULTICAST_LOOP }, };
直接设置是没有办法了,但可以用自己写JNI的方法来添加,因为在socket里有FileDescriptor fd,而里面的int fd 就是对应到内核中socket的 fd, 只要拿到这个fd 就可以调用自己的native方法来设置需要的参数,当然你也可以写自己的 socket, 来封装一个写setsocketoption的native 函数。
- Linux下关于TCP的keep alive的实现源码分析
- TCP的keep-alive小结
- Linux tcp keep-alive
- TCP keep-alive的原理与使用
- 为什么基于TCP的应用需要心跳包(TCP keep-alive原理分析)
- 为什么基于TCP的应用需要心跳包(TCP keep-alive原理分析)
- 为什么基于TCP的应用需要心跳包(TCP keep-alive原理分析)
- socket的keep-alive
- HTTP的Keep-Alive
- muduo的inspect库以及TCP的Keep-Alive时间分析
- 从keep-alive原理分析TCP游戏服务端心跳包的实用功能
- 从keep-alive原理 分析TCP游戏服务端心跳包的实用功能
- 从keep-alive原理 分析TCP游戏服务端心跳包的实用功能
- Linux下关于curl卡死的情况分析
- TCP的KeepAlive和HTTP的Keep-Alive
- http的keep-alive和tcp的keepalive区别
- http的keep-alive和tcp的keepalive区别
- http的keep-alive和tcp的keepalive区别
- phpmyadmin出现“Cannot start session without errors, please check errors given in your PHP and/or webs”
- [Leetcode]Container With Most Water
- 谷歌浏览器javascript调试教程
- 内核阻塞函数中的ERESTARTSYS的定义
- singleton implementation
- Linux下关于TCP的keep alive的实现源码分析
- Java String.split()用法小结
- .NET中原始的ControlPaint类
- Rank() over()的用法
- 使用Netback备份Oracle报ora-27206错误处理
- android PathData生成问题
- 成为高级程序员的10个步骤
- N-Queens II
- 求约稿,创意云《魔女与地下城》征稿大赛火爆开启