select实现超时(套接字IO超时设置)
实现超时的三种方式:
1、SIGALARM信号
void handler(int sig)
{
return 0;
}
signal(SIGALRM,handler);
alarm(5);
int ret=read(fd, buf, sizeof(buf)); //可能会被打断
if(ret==-1 && errno == EINTR)
{
errno=ETIMEOUT;
}
else if (ret>=0)
{
alarm(0);
}
2、
setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, 5);
int ret=read(sock, buf, sizeof(buf)); //超时返回-1
if(ret==-1&&errno==EWOULDBLOCK)
{
...
}
3、select(用select实现超时)
1、read_timeout 函数封装
2、write_timeout函数封装
3、accept_timeot函数封装
4、connect_timeout函数封装
/*
select实现超时检测函数
*/
#include<sysutil.h> //测试代码
int ret;
ret=read_timeout(fd,5);
if(ret==0)
{
read(fd,...); //不会阻塞
}
else if(ret==-1&&errno==ETIMEDOUT)
{
timeout...
}
else
{
ERR_EXIT("red_timeout");
}
/**
*read_timeout读超时检测函数,不含读操作
*wait_seconds 等待超时秒数。如果为0,不检测超时
*成功(返回0,未超时),失败返回-1(超时)并且errno=ETIMDEOUT
*仅仅检测IO是否产生了超时。
*/
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);
}while(ret<0&&errno==EINTR); if(ret==0)//无事件可读,超时
{
ret=-1;
errno=ETIMEDOUT;
}
else if(ret==1)//检测到一个可读事件
ret=0;
} return ret;//wait_seconds==0 直接返回
} /*
写超时检测函数,不含写操作
成功未超时返回0,失败超时返回-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,&write_fdset,NULL,&timeout);
}while(ret<0&&errno==EINTR); if(ret==0)//超时
{
ret=-1;
errno=ETIMEDOUT;
}
else if(ret==1)//检测到一个事件
ret=0;
} return ret;//wait_seconds==0 直接返回
} /*
accept_timeout 带超时的accept函数(包含accept操作)...select在如果是监听套接口(服务器),已完成连接队列不为空时返回
fd:套接字
addr:输出参数,返回对方地址
wait_seconds:等待返回的秒数。如果为0表示正常模式
成功未超时返回已连接套接字,超时返回-1并且errno=ETIMEDOUT
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen);
*/
int accept_timeout(int fd,struct sockaddr_in * addr,unsigned int wait_seconds)
{
int ret;
socklen_t addrlen=sizeof(sockaddr_in);
if(wait_seconds>0)
{
fd_set accept_fdset;
struct timeval timeout;
FD_ZERO(&accept_fdset);
FD_SET(fd,&accept_fdset);
timeout.tv_sev=wait_seconds;
timeout.tv_usec=0;
do
{
ret=select(fd+1,&accept_set,NULL,NULL,&timeout);
}while(ret==-1&&errno==EINTR); if(ret==-1)
return -1;
else if(ret==0)
{
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 error");
return ret;
} /**连接建立三次时,客户端收到ack返回。一次握手往返的时间称为RTT,三次握手 时若有拥塞,系统默认connect返回时间75s超时
连接超时函数
*connect:建立三次握手时connect在服务器返回确认时就返回了。设定自己的连接超时时间。
*fd:套接字
*addr:要连接的对端服务器地址
*wait_seconds:等待超时秒数,如果为0表示正常模式
*成功(未超时)返回0,失败超时返回-1,并且errno=ETIMEDOUT
**/
//将套接口设置为非阻塞模式,防止直接调用connect阻塞
void activate_nonblock(int fd)
{
int ret;
int flags=fcntl(fd,F_GETFL);
if(flags==-1)
ERR_EXIT("fcntl error");
flags|=O_NONBLOCK;//添加非阻塞模式
ret=fcntl(fd,F_SETFL,flags);
if(ret==-1)
ERR_EXIT("fcntl error");
}
//将套接口还原为阻塞模式
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");
}
int connect_timeout(int fd,struct sockadd_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);//已经将套接口设置为非阻塞了,如果不能够立即连接成功,则直接返回EINPROGRESS错误。
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
{
//一旦connect建立连接,套接口就可以写了。
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,可能有两种情况。一种是fd有事件发生,connect建立连接可写了。
*另一种情况是套接字本身产生错误.套接口上发生一个错误待处理,错误可以通过getsockopt指定SO_ERROR选项来获取
*但是select函数没有出错,所以错误信息不能保存到errno
*变量中。只有通过getsockopt来获取套接口fd的错误。
*/
int err;
socklen_t socklen=sizeof(err);
int sockoptret=getsockopt(fd,SOL_SOCKET,SO_ERROR,&err,&socklen);//成功返回0,错误返回-1
if(sockoptret==-1)
{
return -1;
}
if(err==0)//套接字没有错误
ret=0;//返回0成功未超时
else//套接字产生错误
{
errno=err;//套接字错误代码
ret=-1;
}
} }
//网络状况良好,直接成功了,返回ret==0
if(wait_seconds>0)
{
deactivate_nonblock(fd);//重置为阻塞
}
return ret;
}
select实现超时(套接字IO超时设置)的更多相关文章
- 套接字IO超时设置和使用select实现超时管理
在涉及套接字IO超时的设置上有一下3种方法: 1.调用alarm,它在指定的时期满时产生SIGALRM信号.这个方法涉及信号的处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm ...
- Socket编程实践(9) --套接字IO超时设置方法
引:超时设置3种方案 1. alarm超时设置方法 //代码实现: 这种方式较少用 void sigHandlerForSigAlrm(int signo) { return ; } signal(S ...
- windows下的套接字IO模型
一般情况下,IO操作的行为受两种因素的影响: IO操作对象的类型(阻塞还是非阻塞) 获取IO操作结果的方式(同步还是异步). 同步就是指操作的发起和操作结果的获取由调用者完成. 异步指操作发起由调用方 ...
- UNIX网络编程——设置套接字超时
在涉及套接字的I/O操作上设置超时的方法有以下3种: 调用alarm,它在指定超时期时产生SIGALRM信号.这个方法涉及信号处理,而信号处理在不同的实现上存在差异,而且可能干扰进程中现有的alarm ...
- Python网络编程——设定并获取默认的套接字超时时间
Sometimes,you need to manipulate the default values of certain properties of a socket library, for e ...
- windows和linux套接字中的select机制浅析
先来谈谈为什么会出现select函数,也就是select是解决什么问题的? 平常使用的recv函数时阻塞的,也就是如果没有数据可读,recv就会一直阻塞在那里,这是如果有另外一个连接过来,就得一直等待 ...
- Linux编程---套接字
网络相关的东西差点儿都是建立在套接字之上.所以这个内容对于程序猿来说还是蛮重要的啊. 事实上套接字也就是一个特殊的设备文件而已,我始终不能明确为什么要叫套接字.这么个奇怪的名字.只是还是就这样算了吧. ...
- socket - Linux 套接字
总览 #include <sys/socket.h> mysocket = socket(int socket_family, int socket_type, int protocol) ...
- Python套接字编程(1)——socket模块与套接字编程
在Python网络编程系列,我们主要学习以下内容: 1. socket模块与基本套接字编程 2. socket模块的其他网络编程功能 3. SocketServer模块与简单并发服务器 4. 异步编程 ...
随机推荐
- 多测师讲解自动化selenium___定位元素002___高级讲师肖sir
高级自动化测试python+selenium教程手册 --高级讲师肖sir(Harm) 第 2 章8种定位方法 总结: selenium 的 webdriver 提供了八种基本的元素定位方法,前面六种 ...
- 【全网免费VIP观看】哔哩哔哩番剧解锁大会员-集合了优酷-爱奇艺-腾讯-芒果-乐视-ab站等全网vip视频免费破解去广告-高清普清电视观看-持续更新
哔哩哔哩番剧解锁大会员-集合了优酷-爱奇艺-腾讯-芒果-乐视-ab站等全网vip视频免费破解去广告-高清普清电视观看-持续更新 前言 突然想看电视,结果 没有VIP 又不想花钱,这免费的不久来啦. 示 ...
- kali 運行 chrome
0x00前提 已經安裝好google chrome . 0x01 在終端執行命令: google-chrome,發現如圖: 錯誤提示:在root下只能使用 --no-sandbox選項來運行chrom ...
- oh my zsh 常用插件
date: "2020-10-18T12:36:00+08:00" title: "oh my zsh 常用插件" tags: ["zsh" ...
- spring boot:使用poi导出excel电子表格文件(spring boot 2.3.1)
一,什么是poi? 1,poi poi是用来兼容微软文档格式的java api, 它是apache的顶级项目之一, 也是我们在生产环境中导出excel时使用最多的库 2,poi官方网站: http:/ ...
- centos8平台:举例讲解redis6的ACL功能(redis6.0.1)
一,为什么redis6要增加acl功能模块? 什么是acl? 访问控制列表(ACL)是一种基于包过滤的访问控制技术, 它可以根据设定的条件对接口上的数据包进行过滤,允许其通过或丢弃 redis6增加了 ...
- requests设置代理ip
# coding=utf-8 import requests url = "http://test.yeves.cn/test_header.php" headers = { &q ...
- 第十四章 nginx代理配置
一.nginx代理 1.常见模式 1.常见模式:1)正向代理2)反向代理2.区别1)区别在于形式上服务的"对象"不一样2)正向代理代理的对象是客户端,为客户端服务3)反向代理代理 ...
- 探索ParNew和CMS垃圾回收器
前言 上篇文章我们一起分析了JVM的垃圾回收机制,了解了新生代的内存模型,老年代的空间分配担保原则,并简单的介绍了几种垃圾回收器.详细内容小伙伴们可以去看一下我的上篇文章:秒懂JVM的垃圾回收机制. ...
- jsp 记录1 bs/cs
1.jsp = html + js + css + jsp语法 + Java片段: 2.jsp是基于Java语言的,具有跨平台性: 3.jsp编译后的class文件会常驻内存中,运行速度快,对服务器的 ...