用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;
}
原文地址:https://www.cnblogs.com/cfans1993/p/6131894.html