#网络程序设计#(十)套接字I-O的超时设置示例

更多网络程序设计的文章见:目录

套接字I/O超时设置方法

alarm
void handler(int sig)
{
	return 0;
}

signal(SIGALRM, handler);
alarm(5)//设置一个闹钟
int ret = read(fd, buf, sizeof(buf));
if(ret == -1 && errno == EINTR)
{
	errno = ETIMEDOUT;//用来打断read函数
}
else if(ret >= 0)
{
	alarm(0);
}
//问题:闹钟可能有其他用途,会造成冲突
套接字选项
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, 5);
int ret = read(sock, buf, sizeof(buf));
if(ret == -1 && errno == EWOULDBLOCK)
{
	errno = ETIMEDOUT;
}
//问题:有一些tcp不支持这些选项
select

见下文

用select实现超时

read_timeout函数封装
/**
 *read_timeout 读超时检测函数,不含读操作
 *fd:文件描述符
 *wait_seconds:等待超时秒数,如果为0则表示不检测超时
 *成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
int read_timeout(int fd, unsigned int wait_seconds)
{
	int ret = 0;          
	if(wait_seconds > 0) 
	{
		fd_set read_fdset;     
		struct timeval timeout;  
         
		FD_ZERO(&read_fdset);
		FD_SET(fd, &read_fdset);
         
		timeout.tv_sec = wait_seconds;
		timeout.tv_usec = 0;
		do
		{
			ret = select(fd  + 1, &read_fdset, NULL, NULL, &timeout);
             //select会阻塞直到检测到事件或则超时,如果超时,select会返回0,
             //如果超时时间内检测到事件会返回1,如果异常会返回-1
             //如果是由于信号中断引起的异常errno==EINTR
		}while(ret < 0 && errno == EINTR);  
            //如果是有信号引起的异常则继续阻塞select,直到检测到事件或则超时
         
		if(ret == 0) //select超时退出
		{
			ret = -1;
			errno = ETIMEDOUT;
		}
		else if(ret == 1) //select检测到可读事件
			ret = 0;
	}
	return ret;
 }
  • 测试伪代码
int ret;
ret = read_timeout(fd, 5);
if(ret == 0)
	read(fd,……)
else if(ret == -1 && errno == ETIMEDOUT)
	timeout……
else
	ERR_EXIT("read_timeout");
write_timeout函数封装
/**
 *write_timeout 写超时检测函数,不含写操作
 *fd:文件描述符
 *wait_seconds:等待超时秒数,如果为0则表示不检测超时
 *成功(未超时)返回0,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
int write_timeout(int fd, unsigned int wait_seconds)
{
	int ret = 0;          
	if(wait_seconds > 0) 
	{
		fd_set write_fdset;     
		struct timeval timeout;  
         
		FD_ZERO(&write_fdset);
		FD_SET(fd, &write_fdset);
         
		timeout.tv_sec = wait_seconds;
		timeout.tv_usec = 0;
		do
		{
			ret = select(fd  + 1, NULL, NULL, &write_fdset, &timeout);
		}while(ret < 0 && errno == EINTR);  
		         
		if(ret == 0) 
		{
			ret = -1;
			errno = ETIMEDOUT;
		}
		else if(ret == 1) 
			ret = 0;
	}
	return ret;
 }
accept_timeout函数封装
/**
 *accept_timeout 带超时的accept函数
 *fd:文件描述符
 *addr 输出参数,accept返回的对等方的地址结构
 *wait_seconds:等待超时秒数,如果为0则表示正常模式
 *成功(未超时)返回已连接的套接字,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
int accept_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
	int ret = 0;          
	socklen_t addrlen = sizeof(struct sockaddr_in);
	if(wait_seconds > 0) 
	{
		fd_set accept_fdset;      
		struct timeval timeout;
         
		FD_ZERO(&accept_fdset);
		FD_SET(fd, &accept_fdset);
         
		timeout.tv_sec = wait_seconds;
		timeout.tv_usec = 0;
		do
		{
			ret = select(fd + 1, &accept_fdset, NULL, NULL&timeout);
		}while(ret < 0 && errno == EINTR); 
         
		if(ret == -1)// 失败
		{
			return -1;
		}
		if(ret == 0) //select超时退出
		{
			errno = ETIMEDOUT;
			return -1;
		}
	}
	
	if(addr != NULL)//不再阻塞
		ret = accept(fd, (struct sockaddr *)addr, &addrlen);
	else
		ret = accept(fd, NULL, NULL);
	if (ret == -1) 
		ERR_EXIT("accept");
        
	return ret; 
}
connect_timeout函数封装
/* activate_nonblock - 设置IO为非阻塞模式
 * fd: 文件描述符
 */
void activate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");

    flags |= O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}

/* deactivate_nonblock - 设置IO为阻塞模式
 * fd: 文件描述符
 */
void deactivate_nonblock(int fd)
{
    int ret;
    int flags = fcntl(fd, F_GETFL);
    if (flags == -1)
        ERR_EXIT("fcntl");

    flags &= ~O_NONBLOCK;
    ret = fcntl(fd, F_SETFL, flags);
    if (ret == -1)
        ERR_EXIT("fcntl");
}

/**
 *connect_timeout - connect
 *fd:套接字
 *addr 要连接的对方地址
 *wait_seconds:等待超时秒数,如果为0则表示正常模式
 *成功(未超时)返回已连接的套接字,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
int connect_timeout(int fd, struct sockaddr_in *addr, unsigned int wait_seconds)
{
	int ret;
	socklen_t addrlen = sizeof(struct sockaddr_in);

	if (wait_seconds > 0)
		activate_nonblock(fd);

	ret = connect(fd, (struct sockaddr*)addr, addrlen);
	if (ret < 0 && errno == EINPROGRESS)
	{
		fd_set connect_fdset;
		struct timeval timeout;
		FD_ZERO(&connect_fdset);
		FD_SET(fd, &connect_fdset);
		timeout.tv_sec = wait_seconds;
		timeout.tv_usec = 0;
		do
		{
			/* 一但连接建立,套接字就可写 */
			ret = select(fd + 1, NULL, &connect_fdset, NULL, &timeout);
		} while (ret < 0 && errno == EINTR);
		if (ret == 0)
		{
			ret = -1;
			errno = ETIMEDOUT;
		}
		else if (ret < 0)
			return -1;
		else if (ret == 1)
		{
			/* ret返回为1,可能有两种情况,一种是连接建立成功,一种是套接字产生错误,*/
			/* 此时错误信息不会保存至errno变量中,因此,需要调用getsockopt来获取。 */
			int err;
			socklen_t socklen = sizeof(err);
			int sockoptret = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err, &socklen);
			if (sockoptret == -1)
			{
				return -1;
			}
			if (err == 0)
				ret = 0;
			else
			{
				errno = err;
				ret = -1;
			}
		}
	}
	if (wait_seconds > 0)
	{
		deactivate_nonblock(fd);
	}
	return ret;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值