用select实际非阻塞I/O
非阻塞read/write
函数返回0表示可读或可写, -1表示select失败或超时
select返回0表示超时,-1表示读取失败,1表示可读或可写
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;
}
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;
}
非阻塞connect
非阻塞connect需要辅助函数fcntl
select返回值分析同read/write, 当connect连接成功或失败,返回的fd都处于可写状态
所以当select返回1时,需要再次判断connect是否连接成功
判断方法:
1.通过getsockopt获取soketfd是否存在错误
2.通过getpeername判断对方地址是否存在
3.再次调用connect,判断是否返回错误码EISCONN(连接已建立)
void activate_nonblock(int fd){
int ret;
int flags=fcntl(fd,F_GETFL);
if(flags == -1)
err_quit("fcntl");
flags |= O_NONBLOCK;
ret=fcntl(fd,F_SETFL,flags);
if(ret == -1)
err_quit("fcntl");
}
void deactivate_nonblock(int fd){
int ret;
int flags=fcntl(fd,F_GETFL);
if(flags == -1)
err_quit("fcntl");
flags &= ~O_NONBLOCK;
ret=fcntl(fd,F_SETFL,flags);
if(ret == -1)
err_quit("fcntl");
}
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_usec=0;
timeout.tv_sec=wait_seconds;
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){
int err;
socklen_t socklen=sizeof(err);
int sockoptret=getsockopt(fd,SOL_SOCKET,SO_ERROR,&err,&socklen);
if(sockoptret == -1){
return -1;
}else if(err == 0){
return 0;
}else{
errno = err;
ret = -1;
}
}
}
if(wait_seconds > 0)
deactivate_nonblock(fd);
return ret;
}
非阻塞accecpt
int accept_timeout(int fd,struct sockaddr_in *addr,unsigned int wait_seconds){
int ret;
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_usec=0;
timeout.tv_sec=wait_seconds;
do{
ret=select(fd+1,&accept_fdset,NULL,NULL,&timeout);
}while(ret < 0 && errno == EINTR);
if(ret == 0){
errno = ETIMEDOUT;
return -1;
}else if(ret == -1)
return -1;
}
if(addr != NULL)
ret=accept(fd,(struct sockaddr *)addr,&addrlen);
else
ret=accept(fd,NULL,NULL);
if(ret == -1)
err_quit("accept");
return ret;
}