心跳实现_自己动手实现心跳检测

工作中经常使用netcat这个小工具来测试某些端口的连通性,比如5060,5080之类的。于是自己萌生了造轮子的想法,并尝试着给`netcat`加个心跳检测的小功能。

#include 
#include
#include
#include
#include
#include
#include
#include
#include
#include
#define BUF_SIZE 256
int main(){
int fd, connect_fd;
char buffer[BUF_SIZE] = {0};
struct sockaddr_in cliaddr, servaddr;
int cli_addr_len = sizeof(cliaddr);
int n;
fd = socket(AF_INET, SOCK_STREAM, 0);
int opt = 1;

setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt));
bzero((char *)&servaddr, sizeof(servaddr));

servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(10000);
int ret = bind(fd, (struct sockaddr *)&servaddr, sizeof(servaddr));
if (ret < 0){
printf("error %d\n", ret);
exit(1);
}
listen(fd, 5);
connect_fd = accept(fd, (struct sockaddr *)&cliaddr, &cli_addr_len);

while (1) {
char buffer[BUF_SIZE] = {0};
n = read(connect_fd, buffer, BUF_SIZE);
//write(connect_fd, buffer, n);
printf("%s", buffer);
}
close(connect_fd);
close(fd);
return 0;
}

这样一个简单的轮子就造好了,他看起来和netcat实现了类似的功能。 gcc server.c编译。 ./a.out运行

在客户端使用nc ip port连过来,就可以发消息了。抓包查看,有数据就会发没数据就停哪里等着,即使对方死了可能这个链接还会一直在。 

d1c006b9c8af58a71cfc8f919529eb43.png

于是我又想改造下这个小程序,让他可以发心跳检测,弄个通用的补丁。在没有源码情况下替换已知的一些函数,加入想要的keepalive的功能。使用strace ./a.out查看系统调用 

a6e9aacbfdf3d45f0c13fa7962412b5f.png

可以看到只调用 setsockopt 设置了 SO_REUSEADDR 允许端口复用,并没有设置 TCP_KEEPALIVE,那我们 hook 一下 setsockopt 函数调用,让它在设置端口复用的同时设置 TCP_KEEPALIVE

恰好,LD_PRELOAD可以满足需求,他运行在程序运行前优先加载动态链接库,通过这个环境变量,我们可以修改覆盖真正的系统调用,达到我们的目的。代码如下:

#include 
#include
#include
#include
#include
#include
#include
#include

static int (*new)(int , int , int , void *, socklen_t) = NULL;

__attribute__((constructor)) void init() {
new = dlsym(RTLD_NEXT, "setsockopt");
}

int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen) {

new(sockfd, level, optname, &optval, optlen);
// 判断是否是 SO_REUSEADDR
if (level == SOL_SOCKET && optname == SO_REUSEADDR) {
int val = 1; // 设置 SO_KEEPALIVE
int keepidle = 10; // 如该连接在60秒内没有任何数据往来,则进行探测
int keepinterval = 2; // 探测时发包的时间间隔为5 秒
int keepcount = 3;// 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.
new(sockfd, SOL_SOCKET, SO_KEEPALIVE, (void *)&val , sizeof(val));
new(sockfd, SOL_TCP, TCP_KEEPIDLE, (void*)&keepidle , sizeof(keepidle ));
new(sockfd, SOL_TCP, TCP_KEEPINTVL, (void *)&keepinterval , sizeof(keepinterval ));
new(sockfd, SOL_TCP, TCP_KEEPCNT, (void *)&keepcount , sizeof(keepcount ));
return 0;
}
return 0;
}

编译上面的 pach.c 文件为 .so 文件: gcc pach.c -fPIC -D_GNU_SOURCE -shared -ldl -o pach.so 如下命令运行 LD_PRELOAD=./pach.so ./a.out

抓包可以看到已经是每10秒发个包,查看对方是否还活着,完美解决了我的需求 

6fca60bdc271b596d3056348e158cfef.png

即使把他挂在别的有setsockopt函数的程序中,也可以轻松加上心跳功能。

是不是有些心动,自己也动手尝试下吧。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值