在Linux環境下使用TCP的keepalive機制
Linux內置支持keepalive機制,為了使用它,你需要使能TCP/IP網路,為了能夠配置內核在運行時的參數,你還需要procfs和sysctl的支持。
這個過程涉及到keepalive使用的三個用戶驅使的變數:
tcp_keepalive_time:表示的是最近一次數據包(簡單的不含數據的ACKs包)發送與第一次keepalive探針發送之間的時間間隔;當連接被標記為keepalive之後,這個計數器就不會再使用。
tcp_keepalive_intvl:表示的是併發keepalive探針之間的時間間隔。
tcp_keepalive_probes:在確定連接已經斷開並且通知應用層之前所發送的沒有得到回復的探針數。對於這三個參數可以在Linux系統的終端中查看和修改它們的預設值:
Advertisements
查看三個參數的值:
rver3 ~]# cat /proc/sys/net/ipv4/tcp_keepalive_time 7200 [root@Server3 ~]# cat /proc/sys/net/ipv4/tcp_keepalive_intvl 75 [root@Server3 ~]# cat /proc/sys/net/ipv4/tcp_keepalive_probes 9 |
通過命令對這三個參數值進行修改(圖中將三個參數值分別設為:600、60、20):
[root@Server3 ~]# echo 600 > /proc/sys/net/ipv4/tcp_keepalive_time
Advertisements
[root@Server3 ~]# echo 60 > /proc/sys/net/ipv4/tcp_keepalive_intvl
[root@Server3 ~]# echo 6 > /proc/sys/net/ipv4/tcp_keepalive_probes
這種方式重置三個參數值,在系統重啟后三個參數的值又會恢復到默認值,具體如何讓系統永遠記住自己設置的值,可參考其他資料,我們現在關係的是如何在程序中使用keepalive機制並設置這三個參數的值。
在程序中使用keepalive機制
想在程序中使用這種機制,只需要使用setsockopt()函數。
setsockopt()函數用於任意類型、任意狀態套介面的設置選項值。儘管在不同協議層上存在選項,但本函數僅定義了最高的「套介面」層次上的選項。選項影響套介面的操作,諸如加急數據是否在普通數據流中接收,廣播數據是否可以從套介面發送等等。
下面為setsockopt()函數的原型:
#include <sys/types.h>
#include <sys/socket.h>
int setsockopt(int sock, int level, int optname, const void *optval, socklen_t optlen);
參數:
sock:將要被設置或者獲取選項的套接字。
level:選項所在的協議層。
optname:需要訪問的選項名。
optval:對於getsockopt(),指向返回選項值的緩衝。對於setsockopt(),指向包含新選項值的緩衝。
optlen:對於getsockopt(),作為入口參數時,選項值的最大長度。作為出口參數時,選項值的實際長度。對於setsockopt(),現選項的長度。
為了使用函數setsockopt()將某個特定的套接字的keepalive機制打開,參數s是一個socket文件描述符,必須要在這之前使用socket()函數進行創建;參數level必須設置為SOL_SOCKET;第三個參數必須設置為SO_KEEPALIVE;optval參數必須是一個布爾型整型變數,表示想要使能這個選項;最後一個參數表示第四個參數的大小。
程序實現心跳包檢測機制
首先要在備份機和源機之間建立一個專門的socket鏈路來進行心跳檢測。
在源機端,在進行數據遷移之前,會建立一個socket來監聽備份機的連接,並將這個socket和對應的處理函數放入原軟體的io處理列表中,代碼如下:
int listenfd; struct sockaddr_in server_sin; listenfd=socket(AF_INET,SOCK_STREAM,0); server_sin.sin_family=AF_INET; server_sin.sin_addr.s_addr=htonl(INADDR_ANY); server_sin.sin_port=htons(PORT); bind(listenfd,(struct sockaddr *)&server_sin,sizeof(server_sin)); listen(listenfd,1024); qemu_set_fd_handler2(listenfd, NULL, tcpkeepalive_server, NULL, (void *)(intptr_t)listenfd); 該socket對應的處理函數如下: static void tcpkeepalive_server(void *opaque) { int connfd; struct sockaddr_in client_sin; socklen_t client_len=sizeof(client_sin); int listenfd = (intptr_t)opaque; connfd=accept(listenfd,(struct sockaddr *)&client_sin,&client_len); } |
在備份機端,當其開始作為備份機時,會建立socket連接源機的監聽端,並設置對應的tcpkeepalive參數,然後將socket和對應的處理函數加入io處理列表。
我們建立的socket是一個心跳檢測專用鏈路,其上不會有數據流動,只有一種情況備份機端會收到數據,那就是源端出現了故障,tcpkeepalive機制會返回一個錯誤信息,所以捕捉到了這個信息,備份機就會跳轉到對應的處理函數,接替源機開始運行。
對應代碼如下:
int sockfd; struct sockaddr_in sin; int optval; socklen_t optlen = sizeof(optval); sockfd=socket(AF_INET,SOCK_STREAM,0); sin.sin_family=AF_INET; sin.sin_addr.s_addr=addr.sin_addr.s_addr; sin.sin_port=htons(PORT); optval = 1; setsockopt(sockfd, SOL_SOCKET, SO_KEEPALIVE, &optval, optlen); optval = 5; setsockopt(sockfd, SOL_TCP, TCP_KEEPCNT, &optval, optlen); optval = 1; setsockopt(sockfd, SOL_TCP, TCP_KEEPIDLE, &optval, optlen); optval = 1; setsockopt(sockfd, SOL_TCP, TCP_KEEPINTVL, &optval, optlen); connect(sockfd,(struct sockaddr *)&sin,sizeof(sin)); qemu_set_fd_handler2(sockfd, NULL, tcpkeepalive_vm_start, NULL, (void *)(intptr_t)sockfd); |
該socket對應的處理函數很簡單,就是讓備份機開始運行:
static void tcpkeepalive_vm_start(void *opaque)
{
vm_start();
}