linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

软件和网站开发以及相关技术探讨
回复
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#1

帖子 weihua2008 » 2008-08-29 16:15

首先知道setsockopt()函数的KEEPALIVE属性是周期地测试连接是否仍存活,
我上网查了很多资料还是不知道如何使用,
最后硬着头皮自己写了一个服务器端和一个客户端的套接字连接
分别设置了两端的KEEPALIVE属性为打开
服务器端:
#include<stdio.h>
#include<stdlib.h>
#include<sys/socket.h>
#include<error.h>
#include<string.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/wait.h>
#include<arpa/inet.h>
#include<unistd.h>

#define SERVPORT 6000 //设定服务器服务端口为6000
#define MAX_LISTEN_SOCK_NUM 20 //设定可监听套接字的最大个数为20
int main()
{
//sockfd为本地监听套接字标识符,client_fd为客户端套接字标识符
int sockfd,client_fd;
struct sockaddr_in my_addr;
struct sockaddr_in client_addr;

//创建本地监听套接字
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("套接字创建失败!\n");
exit(1);
}

//设置套接字的属性使它能够在计算机重启的时候可以再次使用套接字的端口和IP
int err,sock_reuse=1;
err=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&sock_reuse,sizeof(sock_reuse));
if(err!=0){
printf("套接字可重用设置失败!\n");
exit(1);
}
my_addr.sin_family=AF_INET;
my_addr.sin_port=htons(SERVPORT);
my_addr.sin_addr.s_addr=INADDR_ANY;
bzero(&(my_addr.sin_zero),8);
//绑定套接字
if(bind(sockfd,(struct sockaddr *)&my_addr,sizeof(struct sockaddr))==-1){
perror("绑定失败!\n");
exit(1);
}
//设置监听
if((listen(sockfd,MAX_LISTEN_SOCK_NUM))==-1){
perror("设置监听失败!\n");
exit(1);
}
printf("套接字进入监听状态,等待请求连接:\n");

//int time_to_quit=1;
//while(time_to_quit){ //可以通过设置time_to_quit来主动关闭服务器端
while(1){

//有连接请求时进行连接
socklen_t sin_size=sizeof(struct sockaddr_in);
if((client_fd=accept(sockfd,(struct sockaddr *)&client_addr,&sin_size))==-1){
perror("接受连接失败!\n");
continue;
}

int opt;
socklen_t len=sizeof(int);
if((getsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char*)&opt,&len))==0){
printf("SO_KEEPALIVE Value: %d\n", opt);
}
printf("接到一个来自%s的连接\n",inet_ntoa(client_addr.sin_addr));
//创建子进程来处理已连接的客户端套接字

if(send(client_fd,"您好,您已经连接成功!\n",50,0)==-1){
perror("发送通知信息失败!\n");
exit(0);
}

}
close(client_fd);

return 0;
}
客户端:
#include<stdio.h>
#include<stdlib.h>
#include<error.h>
#include<string.h>
#include<netdb.h>
#include<sys/types.h>
#include<netinet/in.h>
#include<sys/socket.h>
#include<unistd.h>
#include<arpa/inet.h>
#define MAXDATASIZE 100 /*每次最大数据传输量*/
int main()
{
int sockfd,nbytes,serv_port;
char buf_serv_ip[16],buf[26];
struct sockaddr_in serv_addr;
if((sockfd=socket(AF_INET,SOCK_STREAM,0))==-1){
perror("创建套接字失败!\n");
exit(1);
}
//创建套接字成功后设置其可重用的属性
int KeepAlive=1;
socklen_t KPlen=sizeof(int);
if(setsockopt(sockfd,SOL_SOCKET,SO_KEEPALIVE,(char *)&KeepAlive,KPlen)!=0){
perror("设置周期测试连接是否仍存活失败!\n");
exit(1);
}
printf("请输入要连接主机的IP地址:\n");
scanf("%s",buf_serv_ip);
printf("请输入要连接主机的端口号:\n");
scanf("%d",&serv_port);
serv_addr.sin_family=AF_INET;
serv_addr.sin_addr.s_addr=inet_addr(buf_serv_ip);
serv_addr.sin_port=htons(serv_port);
bzero(&(serv_addr.sin_zero),8);
if(connect(sockfd,(struct sockaddr *)&serv_addr,sizeof(struct sockaddr))==-1){
perror("连接服务器失败!\n");
exit(1);
}
printf("连接服务器成功!\n");
//在此处可以先接受判断将要接受数据的长度再创建数组
if((nbytes=recv(sockfd,buf,26,0))==-1){
perror("接受数据失败!\n");
exit(1);
}
buf[nbytes]='\0';
printf("接受的数据为:%s\n",buf);
close(sockfd);
return 0;
}
分别在两个终端运(在同一个主机上)行:先启动服务器端;再启动客户端,向服务器端请求连接,连接成功后,客户端套接字关闭,服务器端套接字始终打开,等待两个小时多也没有反应,

肯请高手指点:
(1)这样做能达到预期报告连接是否仍存在的效果吗,
(2)不能的话应该如何做,
(3)还有getsockopt()的相关功能又是什么哪?在上面的程序中没有用到该函数
(4)看了unix网络编程上的一点关于该问题的解释还是不明白,因为她提到要两个小时才回应,但等了撒小时也没有反应,她还说可以缩短时间,我却不知道如何缩短
学无止境
lew0001
帖子: 31
注册时间: 2008-06-06 21:45

#2

帖子 lew0001 » 2008-08-30 23:49

(1)man 7 tcp,搜索keepalive,可以发现默认设置下要在没有收到对端数据两小时后开始发送keep alive包,如果没有回应再过十一分钟后才会认为socket已断开。
(2)可以定时向对端发送心跳消息,一段时间(至少两倍于心跳消息发送间隔时间)没有收到对端发来的心跳消息或者发送心跳消息失败则认为连接已断开。
(3) man getsockopt,man 7 tcp
(4)参见(1),不过最好还是采用(2)的方法
ls /proc/sys/net/ipv4/tcp_keepalive_*
与keep alive相关的就是这几个内核参数
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

#3

帖子 weihua2008 » 2008-09-01 8:31

lew0001
我一开始是想用心跳包,可是查了半天还是没有查到,具体的用法,大侠知道额话,还望赐教!
谢谢
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

#4

帖子 weihua2008 » 2008-09-01 9:32

lew0001
还是用KEEPALIVE属性的话。两个小时肯定是等不起,听说可以设置等待时间的值,比如可以改为5秒钟就发送KEEPALIVE包,您知道如何修改该属性吗?
xuxian02092213
帖子: 1
注册时间: 2008-10-09 20:18

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#5

帖子 xuxian02092213 » 2008-10-09 20:24

err=setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,(char *)&sock_reuse,sizeof(sock_reuse));
if(err!=0){
printf("套接字可重用设置失败!\n");
exit(1);
}


不觉得这句话有问题吗?设置的是套接字不能同一个正在被其他socket使用本地地址绑定在一起,表述有点差异。

别不是语言的差异吧, :em06
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#6

帖子 weihua2008 » 2008-10-10 8:49

xuxian02092213,
linux所提供的库中有个错误,你不能为一个套接字重新启用同一个端口,即使在你正常关闭套接字后。
如:你写了一个服务器在一个套接字上等待连接请求的程序,首次打开在套接字上监听没有问题,无论如何,总有一些原因使你的程序要重启,然而重启后你就不能把它绑定到原来的端口上了,具体的linux内核错误你自己再查查,
为了解决这个问题,提供了这种方法
understand?
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#7

帖子 weihua2008 » 2008-11-02 19:42

上述问题也算是基本解决,就是说对于socket的keepalive属性的默认值可以利用函数进行修改,
Tcp是面向连接的,在实际应用中通常都需要检测连接是否还可用.如果不可用,可分为:

a. 连接的对端正常关闭.

b. 连接的对端非正常关闭,这包括对端设备掉电,程序崩溃,网络被中断等.这种情况是不能也无法通知对端的,所以连接会一直存在,浪费国家的资源.

tcp协议栈有个keepalive的属性,可以主动探测socket是否可用,不过这个属性的默认值很大.

全局设置可更改/etc/sysctl.conf,加上:

net.ipv4.tcp_keepalive_intvl = 20
net.ipv4.tcp_keepalive_probes = 3
net.ipv4.tcp_keepalive_time = 60 网管论坛bbs_bitsCN_com

在程序中设置如下:

#include <netinet/tcp.h>

int keepAlive = 1; // 开启keepalive属性
int keepIdle = 60; // 如该连接在60秒内没有任何数据往来,则进行探测
int keepInterval = 5; // 探测时发包的时间间隔为5 秒
int keepCount = 3; // 探测尝试的次数.如果第1次探测包就收到响应了,则后2次的不再发.

setsockopt(rs, SOL_SOCKET, SO_KEEPALIVE, (void *)&keepAlive, sizeof(keepAlive));
setsockopt(rs, SOL_TCP, TCP_KEEPIDLE, (void*)&keepIdle, sizeof(keepIdle));
setsockopt(rs, SOL_TCP, TCP_KEEPINTVL, (void *)&keepInterval, sizeof(keepInterval));
setsockopt(rs, SOL_TCP, TCP_KEEPCNT, (void *)&keepCount, sizeof(keepCount));

虽然是设置成功但是,不太清楚如何验证该属性是不是在起作用,这个测试工作还没有得到很好的测试,有较好的测试方法,麻烦过客在该页上踩上几脚
wqfhenanxc
帖子: 1
注册时间: 2008-03-06 10:00

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#8

帖子 wqfhenanxc » 2008-12-21 21:00

我认为你没有得到预期结果是因为:
keepalive参数应该在服务器端设置,而你是在客户端设置了。
实验时你关闭了客户端,服务器没设置keepalive当然不会去测试并关闭连接了。
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#9

帖子 weihua2008 » 2008-12-22 9:54

我会再测试,但先在还不敢说你说的是对
至少不错,我认为,我好像对于设置keepalive属性后的后继工作做的不是很到位
lw02nju
帖子: 4
注册时间: 2007-03-25 20:22

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#10

帖子 lw02nju » 2009-11-23 17:44

不知道楼主这个问题解决没,我现在也遇到了同样的情况,client上设置socket选项是有效的,服务器断设置死活不行。
问题是在服务器端设置keepalive选项的话,你必须在accept之后,才能得到与客户端通信的socket,而要想用setsockopt,好像必须在accept和connect之前调用。这样的话就造成了能设置选项的时候没有socket对象,有了socket后再设置就无效了,不知道这个问题如何去解决。
weihua2008
帖子: 448
注册时间: 2008-07-10 15:08

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#11

帖子 weihua2008 » 2009-11-24 10:54

lw02nju,
想问你在客户端设置有效是什么意思?
lw02nju
帖子: 4
注册时间: 2007-03-25 20:22

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#12

帖子 lw02nju » 2009-11-25 17:05

weihua2008 写了:lw02nju,
想问你在客户端设置有效是什么意思?
我是指对客户端的socket进行setsockopt。
不过问题解决了,后来发现实际上是有效的,不过开发的时候拿虚拟机来模拟宕机就不行,应该是跟虚拟机的实现机制有关。
basncy
帖子: 321
注册时间: 2009-11-19 10:40

Re: linux下setsockopt()与getsockopt()函数的使用(关于KEEPALIVE属性)

#13

帖子 basncy » 2010-07-04 0:40

好像是一年前的帖子了,收藏,说不定我会用到
回复