gcc g++现在是gnu中最主要和最流行的c c++编译器 。g++是c++的命令,以.cpp为主,对于c语言后缀名一般为.c。这时候命令换做gcc即可。其实是无关紧要的。其实编译
(一)使用alarm 函数设置超时
#include
unsigned int alarm(unsigned int seconds);
void sigHandlerForSigAlrm(int signo)
{
return ;
}
signal(SIGALRM, sigHandlerForSigAlrm);
alarm(5);
int ret = read(sockfd, buf, sizeof(buf));
if (ret == -1 && errno == EINTR)
{
// 阻塞并且达到了5s,超时,设置返回错误码
errno = ETIMEDOUT;
}
else if (ret >= 0)
{
// 正常返回(没有超时), 则将闹钟关闭
alarm(0);
}
如果read一直处于阻塞状态被SIGALRM信号中断而返回,则表示超时,否则未超时已读取到数据,取消闹钟。但这种方法不常用,因为有时可能在其他地方使用了alarm会造成混乱。
调用setsockopt设置读/写超时时间
/示例: read超时
int seconds = 5;
if (setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, &seconds, sizeof(seconds)) == -1)
err_exit("setsockopt error");
int ret = read(sockfd, buf, sizeof(buf));
if (ret == -1 && errno == EWOULDBLOCK)
{
// 超时,被时钟信号打断
errno = ETIMEDOUT;
} SO_RCVTIMEO是接收超时,SO_SNDTIMEO是发送超时。这种方式也不经常使用,因为这种方案不可移植,并且有些套接字的实现不支持这种方式。
(三)使用select函数实现超时
#include
int select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
struct timeval{
long tv_sec; /*秒 */
long tv_usec; /*微秒 */
}
select函数是在linux编程中很重要的一个函数,他有很多的功能,控制读、写、异常的集合,当然还有设置超时。
下面我们依次封装read_timeout、write_timeout、accept_timeout、connect_timeout四个函数,来了解select在超时设置方面的使用。
1.read_timeout
/**
*read_timeout - 读超时检测函数, 不包含读操作
*@fd: 文件描述符
*@waitSec: 等待超时秒数, 0表示不检测超时
*成功(未超时)返回0, 失败返回-1, 超时返回-1 并且 errno = ETIMEDOUT
**/
int read_timeout(int fd, long waitSec)
{
int returnValue = 0;
if (waitSec > 0)
{
fd_set readSet;
FD_ZERO(&readSet);
FD_SET(fd,&readSet); //添加
struct timeval waitTime;
waitTime.tv_sec = waitSec;
waitTime.tv_usec = 0;
//将微秒设置为0(不进行设置),如果设置了,时间会更加精确
do
{
returnValue = select(fd+1,&readSet,NULL,NULL,&waitTime);
}
while(returnValue < 0 && errno == EINTR); //等待被(信号)打断的情况, 重启select
if (returnValue == 0) //在waitTime时间段中一个事件也没到达,超时
{
returnValue = -1; //返回-1
errno = ETIMEDOUT;
}
else if (returnValue == 1) //在waitTime时间段中有事件产生
returnValue = 0; //返回0,表示成功
// 如果(returnValue == -1) 并且 (errno != EINTR), 则直接返回-1(returnValue)
}
return returnValue;
}
fd_set rset;
int fd;
FD_ZERO(&rset);
FD_SET(fd, &rset);
FD_SET(stdin, &rset);
if(FD_ISSET(fd, &rset)
{ ... }
2.write_timeout
实现方式和read_timeout基本相同。
/**
*write_timeout - 写超时检测函数, 不包含写操作
*@fd: 文件描述符
*@waitSec: 等待超时秒数, 0表示不检测超时
*成功(未超时)返回0, 失败返回-1, 超时返回-1 并且 errno = ETIMEDOUT
**/
int write_timeout(int fd, long waitSec)
{
int returnValue = 0;
if (waitSec > 0)
{
fd_set writeSet;
FD_ZERO(&writeSet);
//清零
FD_SET(fd,&writeSet); //添加
struct timeval waitTime;
waitTime.tv_sec = waitSec;
waitTime.tv_usec = 0;
do
{
returnValue = select(fd+1,NULL,&writeSet,NULL,&waitTime);
} while(returnValue < 0 && errno == EINTR); //等待被(信号)打断的情况
if (returnValue == 0) //在waitTime时间段中一个事件也没到达
{
returnValue = -1; //返回-1
errno = ETIMEDOUT;
}
else if (returnValue == 1) //在waitTime时间段中有事件产生
returnValue = 0; //返回0,表示成功
}
return returnValue;
} 3.accept_timeout
/**
*accept_timeout - 带超时的accept
*@fd: 文件描述符
*@addr: 输出参数, 返回对方地址
*@waitSec: 等待超时秒数, 0表示不使用超时检测, 使用正常模式的accept
*成功(未超时)返回0, 失败返回-1, 超时返回-1 并且 errno = ETIMEDOUT
**/
int accept_timeout(int fd, struct sockaddr_in *addr, long waitSec)
{
int returnValue = 0;
if (waitSec > 0)
{
fd_set acceptSet;
FD_ZERO(&acceptSet);
FD_SET(fd,&acceptSet); //添加
struct timeval waitTime;
waitTime.tv_sec = waitSec;
waitTime.tv_usec = 0;
do
{
returnValue = select(fd+1,&acceptSet,NULL,NULL,&waitTime);
}
while(returnValue < 0 && errno == EINTR);
if (returnValue == 0) //在waitTime时间段中没有事件产生
{
errno = ETIMEDOUT;
return -1;
}
else if (returnValue == -1) // error
return -1;
}
/**select正确返回:
表示有select所等待的事件发生:对等方完成了三次握手,
客户端有新的链接建立,此时再调用accept就不会阻塞了
*/
socklen_t socklen = sizeof(struct sockaddr_in);
if (addr != NULL)
returnValue = accept(fd,(struct sockaddr *)addr,&socklen);
else
returnValue = accept(fd,NULL,NULL);
return returnValue;
}
4.connect_timeout
(1)我们为什么需要这个函数?