在伺服器端啟用 TCP keepalive
這是一個客戶端 - 伺服器示例。該程序在子程序中的父程序和伺服器中分叉並執行客戶端:
- 客戶端連線到伺服器並等待伺服器退出;
- 伺服器接受來自客戶端的連線,啟用 keepalive,並等待任何訊號。
使用 socket(7)
和 tcp(7)
手冊頁中描述的以下選項配置 Keepalive :
SO_KEEPALIVE
- 允許傳送保持活動訊息TCP_KEEPIDLE
- TCP 開始傳送 keepalive 探測之前連線需要保持空閒的時間(以秒為單位)TCP_KEEPINTVL
- 各個 keepalive 探測器之間的時間(以秒為單位)TCP_KEEPCNT
- TCP 在丟棄連線之前應傳送的最大 keepalive 探測數
原始碼:
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/signal.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <netinet/tcp.h>
#define check(expr) if (!(expr)) { perror(#expr); kill(0, SIGTERM); }
void enable_keepalive(int sock) {
int yes = 1;
check(setsockopt(
sock, SOL_SOCKET, SO_KEEPALIVE, &yes, sizeof(int)) != -1);
int idle = 1;
check(setsockopt(
sock, IPPROTO_TCP, TCP_KEEPIDLE, &idle, sizeof(int)) != -1);
int interval = 1;
check(setsockopt(
sock, IPPROTO_TCP, TCP_KEEPINTVL, &interval, sizeof(int)) != -1);
int maxpkt = 10;
check(setsockopt(
sock, IPPROTO_TCP, TCP_KEEPCNT, &maxpkt, sizeof(int)) != -1);
}
int main(int argc, char** argv) {
check(argc == 2);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = htons(12345);
check(inet_pton(AF_INET, argv[1], &addr.sin_addr) != -1);
int server = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
check(server != -1);
int yes = 1;
check(setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)) != -1);
check(bind(server, (struct sockaddr*)&addr, sizeof(addr)) != -1);
check(listen(server, 1) != -1);
if (fork() == 0) {
int client = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
check(client != -1);
check(connect(client, (struct sockaddr*)&addr, sizeof(addr)) != -1);
printf("connected\n");
pause();
}
else {
int client = accept(server, NULL, NULL);
check(client != -1);
enable_keepalive(client);
printf("accepted\n");
wait(NULL);
}
return 0;
}
可以使用 tcpdump
監控 Keepalive 資料包。
用法示例:
$ ./a.out 127.0.0.1 &
[1] 14010
connected
accepted
$ tcpdump -n -c4 -ilo port 12345
dropped privs to tcpdump
tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
listening on lo, link-type EN10MB (Ethernet), capture size 262144 bytes
18:00:35.173892 IP 127.0.0.1.12345 > 127.0.0.1.60998: Flags [.], ack 510307430, win 342, options [nop,nop,TS val 389745775 ecr 389745675], length 0
18:00:35.173903 IP 127.0.0.1.60998 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 389745775 ecr 389745075], length 0
18:00:36.173886 IP 127.0.0.1.12345 > 127.0.0.1.60998: Flags [.], ack 1, win 342, options [nop,nop,TS val 389745875 ecr 389745775], length 0
18:00:36.173898 IP 127.0.0.1.60998 > 127.0.0.1.12345: Flags [.], ack 1, win 342, options [nop,nop,TS val 389745875 ecr 389745075], length 0
4 packets captured
8 packets received by filter
0 packets dropped by kernel