In Linux, the implementation source code of TCP keep alive is analyzed, and keepalive
Keep Alive under TCP
We often say that the TCP keep alive is to ensure the validity of the connection, send a test packet at a certain interval, and confirm whether the connection is valid according to the reply. Upper-layer applications usually provide their own heartbeat detection mechanism, while the Linux kernel itself also provides a way to ensure the connection validity at the kernel level.
In the sock function, you can set whether to enable the keep alive switch. By default, the socket is established to disable keep alive. The Code is as follows:
optval = 1; optlen = sizeof(optval); if(setsockopt(s, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen) < 0) { perror("setsockopt()"); close(s); exit(EXIT_FAILURE); }
Keep Alive Control Parameters
The tcp_keepalive_time parameter controls the maximum idle time of the keep alive.
Tcp_keepalive_probes Parameter
When the maximum space is exceeded, the kernel will try to issue a probe package to confirm that the client is alive. This parameter controls the number of attempts
When the tcp_keepalive_intvl parameter exceeds the maximum idle time, the kernel sends a probe packet. If no response is received, this parameter controls the time of the next probe packet.
How to Implement timer_list in the Keep Alivesock struct in Linux
In the sock struct, the struct sk_timer of timer_list exists. refer to the following structure.
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};
The timer_list struct is a common timer execution linked list in sock. entry represents the head of the linked list, expires represents the expiration time, and function is the execution function.
Register the keepalive processing function
Void Merge (struct sock * sk, void (* retransmit_handler) (unsigned long), void (* delack_handler) (unsigned long), void (* keepalive_handler) (unsigned long )) {struct handler * icsk = inet_csk (sk); setup_timer (& icsk-> handler, retransmit_handler, (unsigned long) sk); setup_timer (& icsk-> handler, delack_handler, (unsigned long) sk); setup_timer (& sk-> sk_timer, keepalive_handler, (unsigned long) sk ); // registers the function. In sk_timer, icsk-> icsk_pending = icsk-> icsk_ack.pending = 0 ;}
When the connection is complete (That is, when the handshake is successfulIn the newly generated sock's sk_timer struct, The keepalive_handler function is registered.
void tcp_init_xmit_timers(struct sock *sk){inet_csk_init_xmit_timers(sk, &tcp_write_timer, &tcp_delack_timer, &tcp_keepalive_timer);}
The keepalive_handler function is the tcp_keepalive_timer function.
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;} .....}
The above is just a part of the code, focusing on the implementation of the parameters mentioned above. The code first checks whether the sock parameter SO_KEEPALIVE is set in sock, that is, the flag: SOCK_KEEPOPEN in sock.
If the SO_KEEPALIVE of the socket is set, check the timestamp again. Compare the difference between the timestamp of the last received packet and the current timestamp with the keepalive_time parameter. If it has timed out, check the number of times that the probe package has failed. If the number is greater than keepalive_probes, issue a reset package, write an error report, and disable sock.
If the number of times is smaller than the number of probe packets set, the probe packet is sent and the timestamp of the next verification is set to keepalive_intvl, not keepalive_time.
Note: keepalive_intvl only controls the time when the next verification is triggered.
The end time of the calculation is invalid. There are two scenarios: N.
A. The keepalive_intvl time is later than keepalive_time.
N = keepalive_time + keepalive_intvl * keepalive_probes
B. keepalive_intvl takes less time than keepalive_time.
N = keepalive_time + keepalive_time * keepalive_probes
This is why, in the default settings, it takes 12 hours for the connection to be disconnected from 7200*6 because keepalive_time, keepalive_probes, and keepalive_intvl are set in the connection code.
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))
The three parameters corresponding to TCP_KEEPIDLE --> keepalive_time, TCP_KEEPINTVL --> keepalive_intvl, TCP_KEEPCNT --> keepalive_probesthe three parameters also have the maximum value protection, the maximum value of TCP_KEEPIDLE is 32767. The maximum value of TCP_KEEPINTVL is 32767. The maximum value of TCP_KEEPCNT is 127.
JAVA does not provide modifications to these parameters
There will be a lot of timer during the processing of the timer and the TCP connection, for example, the timer for clearing the accept queue mentioned in the previous blog, ack timer, if you are interested, refer to tcp_timer.c. The main function of the timer is to call the program at a fixed time. In keep alive, the probe package is sent regularly to determine the validity of the package.
Set Keep alive in JAVA
In Java, only open keep alive is allowed, but several related parameters of keep alive are not allowed. in Java, open keep alive on the client and directly call Socket. the setKeepAlive function, but the ServerSocket on the server does not allow the keep alive switch. You can only set it when you accept a new connected socket.
How can I set the required parameters in java? Here is just a thought
In the last JNI call in java, constant array protection is set. Only parameters in the array can be set to socket.
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 }, };
There is no way to directly set it, but you can add it by writing JNI, because there is FileDescriptor fd in the socket, and the int fd in it is the fd corresponding to the socket in the kernel, once you get this fd, you can call your native method to set the required parameters. Of course, you can also write your own socket to encapsulate a native function that writes setsocketoption.