linux網路編程之socket(十一)通訊端I/O逾時設定方法和用select實現逾時

來源:互聯網
上載者:User

一、使用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;}

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.