加入收藏 | 设为首页 | 会员中心 | 我要投稿 佛山站长网 (https://www.0757zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 服务器 > 搭建环境 > Linux > 正文

linux网络编程之socket(十一)套接字I/O超时设置方法和用select实现超时

发布时间:2013-09-14 20:21:42 所属栏目:Linux 来源:站长网
导读:一、使用alarm 函数设置超时 void handler(int sig){}signal(SIGALRM, handler); alarm(5);int ret = read(fd, buf, sizeof(buf));if (ret == -1 errno == EINT
一、使用alarm 函数设置超时

void handler(int sig)
{
}
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);
.................

程序大概框架如上所示,如果read在5s内被SIGALRM信号中断而返回,则表示超时,否则未超 时已读取到数据,取消闹钟。但这种方法不常用,因为有时可能在其他地方使用了alarm会造成混乱。

二、使用套接 字选项SO_SNDTIMEO、SO_RCVTIMEO

setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, 5);
    
int ret = read(sock, buf, sizeof(buf));
if (ret == -1 && errno == EWOULDBLOCK)
    errno = ETIMEOUT;
..........

即使用setsockopt 函数进行设置,但这种方法可移植性比较差,不是每种系统实现都有这些选项。

三、使用select 实现超时

下面程序包含read_timeout、write_timeout、accept_timeout、 connect_timeout 四个函数封装

/*************************************************************************
    > File Name: sysutil.c
    > Author: Simba
    > Mail: dameng34@163.com
    > Created Time: Sat 02 Mar 2013 10:53:06 PM CST
 ************************************************************************/
    
#include "sysutil.h"
    
/* 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检测到可读事件发送,则此时调用read不会阻塞
        }
        while (ret < 0 && errno == EINTR);
    
        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret == 1)
            return 0;
    
    }
    
    return ret;
}
    
/* 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, &write_fdset, NULL, &timeout);
        }
        while (ret < 0 && errno == EINTR);
    
        if (ret == 0)
        {
            ret = -1;
            errno = ETIMEDOUT;
        }
        else if (ret == 1)
            return 0;
    
    }
    
    return ret;
}
    
/* accept_timeout - 带超时的accept
 * fd: 套接字
 * addr: 输出参数,返回对方地址
 * wait_seconds: 等待超时秒数,如果为0表示正常模式
 * 成功(未超时)返回已连接套接字,失败返回-1,超时返回-1并且errno = ETIMEDOUT
 */
    
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_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;
        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("accpet error");
    
    return ret;
}
    
/* activate_nonblock - 设置IO为非阻塞模式
 * fd: 文件描述符
 */
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");
}
    
/* deactivate_nonblock - 设置IO为阻塞模式
 * fd: 文件描述符
 */
void deactivate_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");
}
    
/* connect_timeout - 带超时的connect
 * fd: 套接字
 * addr: 输出参数,返回对方地址
 * wait_seconds: 等待超时秒数,如果为0表示正常模式
 * 成功(未超时)返回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)
        {
            errno = ETIMEDOUT;
            return -1;
        }
        else if (ret < 0)
            return -1;
    
        else if (ret == 1)
        {
            /* ret返回为1,可能有两种情况,一种是连接建立成功,一种是套接字产生错误
             * 此时错误信息不会保存至errno变量中(select没出错),因此,需要调用
             * 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;
}

(编辑:佛山站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读