通过select 和状态EINPROGRESS 实现socket 连接超时判断

通过select 和状态EINPROGRESS 实现socket 连接超时判断调用connect连接一般的超时时间是75s,但是在程序中我们一般不希望等这么长时间采取采取动作。可以在调用connect之前设置套接字非阻塞,然后调用connect,此时connect会立刻返回,如果连接成功则直接返回0(成功),如果没有连接成功,也会立即返回并且会设置errno为EINPROCESS,这并不是一个致命错误,仅仅是告知你已经在连接了,你只要判断是它就继续执行后面的逻辑就行

大家好,又见面了,我是你们的朋友全栈君。

调用connect连接一般的超时时间是75s, 但是在程序中我们一般不希望等这么长时间采取采取动作。 可以在调用connect之前设置套接字非阻塞,然后调用connect,此时connect会立刻返回, 如果连接成功则直接返回0(成功), 如果没有连接成功,也会立即返回并且会设置errno为EINPROCESS,这并不是一个致命错误,仅仅是告知你已经在连接了,你只要判断是它就继续执行后面的逻辑就行了,比如select.通过select设置超时来达到为connect设定超时的目的. 下面的代码显示这个过程。

bool timeout_connect(const string& _host, uint16_t _port, uint32t _timeout, int32_t& _sockfd)

{

#define CLOSE_SOCK_AND_RETURN_FALSE(so)    close(so); return false;

    if (_sockfd != -1) return true;

    

    int sockfd;

    struct sockaddr_in serv_addr;

    memset(&serv_addr, 0, sizeof(serv_addr));

    serv_addr.sin_family = AF_INET;

    serv_addr.sin_port = htons(_port);

    serv_addr.sin_addr.s_addr = inet_addr(_host.c_str());

    if ((sockfd=socket(AF_INET, SOCK_STREAM, 0)) == -1)

    {

        return false;

    }

    // get origin sockfd flag

    int flags = fcntl(sockfd, F_GETFL);

    if (flags == -1)

    {

        CLOSE_SOCK_AND_RETURN_FALSE(sockfd);

    }

    

    // set sockfd to non-block mode

    int retcode = fcntl(sockfd, F_SETFL, flags|O_NONBLOCK);

    if (retcode == -1)

    {

        CLOSE_SOCK_AND_RETURN_FALSE(sockfd);

    }

    if (::connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)

    {

        if (errno == EINPROGRESS) // EINPROGRESS means connection is in progress, normally the socket connecting timeout is 75s. after the socket fd is ready to read.

                                                            // means the connecting of the socket fd is established.

        {

            int err;

            int len = sizeof(int);

            fd_set wds;

            struct timeval tm;

            tm.tv_sec = _timeout;

            tm.tv_usec = 0;

            FD_ZERO(&wds);

            FD_SET(sockfd, &wds);

            if (select(sockfd + 1, NULL, &wds, NULL, &tm) > 0)  // “>0” means sockfd ready to read, “=0” means timeout cause retrun, “<0” means error.

            {

                retcode = getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &err, (socklen_t *)&len);

                if (retcode == -1 || err != 0)

                {

                    CLOSE_SOCK_AND_RETURN_FALSE(sockfd);

                }

            }

            else

            {

                CLOSE_SOCK_AND_RETURN_FALSE(sockfd);

            }

        }

        else

        {

            CLOSE_SOCK_AND_RETURN_FALSE(sockfd);

        }

    }

    retcode = fcntl(sockfd, F_SETFL, flags);  // trun back the mode of sockfd to block.

    if (retcode == -1)

    {

        CLOSE_SOCK_AND_RETURN_FALSE(sockfd);

    }

    struct timeval readtimeout;

    readtimeout.tv_sec = _timeout;

    readtimeout.tv_usec = 0;

    retcode = setsockopt(sockfd, SOL_SOCKET, SO_RCVTIMEO, (void *)&readtimeout, sizeof(readtimeout)); // set socket read timeout.

    if (retcode == -1)

    {

        CLOSE_SOCK_AND_RETURN_FALSE(sockfd);

    }

#undef CLOSE_SOCK_AND_RETURN_FALSE

    _sockfd = sockfd;

    return true;

}

通过SO_RCVTIMEO 设置连接超时

SO_RCVTIMEO和SO_SNDTIMEO套接口选项可以给套接口的读和写,来设置超时时间,

一、在unix网络编程中,说是他们只能用于读和写,而像 accept和connect都不能用他们来设置.

可是我在阅读内核源码的过程中看到,在linux中,accept和connect可以分别用 SO_RCVTIMEO和SO_SNDTIMEO套接口来设置超时,这里他们的超时时间也就是sock的sk_rcvtimeo和sk_sndtimeo 域.accept和connect的相关代码我前面都介绍过了,这里再提一下.其中accept的相关部分在inet_csk_accept中,会调用 sock_rcvtimeo来取得超时时间(如果是非阻塞则忽略超时间).而connect的相关代码在inet_stream_connect中通过调用sock_sndtimeo来取得超时时间(如果非阻塞则忽略超时时间).

SO_RCVTIMEO和SO_SNDTIMEO ,它们分别用来设置socket接收数据超时时间和发送数据超时时间。
因此,这两个选项仅对与数据收发相关的系统调用有效,这些系统调用包括:send, sendmsg, recv, recvmsg, accept, connect 。
这两个选项设置后,若超时, 返回-1,并设置errno为EAGAIN或EWOULDBLOCK.
其中connect超时的话,也是返回-1, 但errno设置为EINPROGRESS

  #include <stdio.h>  
    #include <stdlib.h>  
    #include <unistd.h>  
    #include <string.h>  
    #include <errno.h>  
    #include <assert.h>  
    #include <fcntl.h>  
    #include <sys/types.h>  
    #include <sys/socket.h>  
    #include <netinet/in.h>  
    #include <arpa/inet.h>  
      
    //超时连接  
    int timeout_connect(const char *ip, int port, int time);  
      
    int main(int argc, char **argv)  
    {  
        if (argc != 3) {  
            fprintf(stderr, “Usage: %s ip port\n”, argv[0]);  
            return 1;  
        }  
          
        const char *ip = argv[1];  
        int port = atoi(argv[2]);  
          
        int sockfd = timeout_connect(ip, port, 10);  
        if (sockfd < 0)  
            return 1;  
          
          
        return 0;  
    }  
      
    int timeout_connect(const char *ip, int port, int time)  
    {  
        int ret = 0;  
        int error;  
          
        struct sockaddr_in address;  
        bzero(&address, sizeof(address));  
        address.sin_family = AF_INET;  
        address.sin_port = htons(port);  
        inet_pton(AF_INET, ip, &address.sin_addr);  
          
        int sockfd = socket(PF_INET, SOCK_STREAM, 0);  
        if (sockfd == -1)  
            return -1;  
          
        //超时时间  
        struct timeval timeout;  
        timeout.tv_sec = time;  
        timeout.tv_usec = 0;  
          
        socklen_t len = sizeof(timeout);  
        ret = setsockopt(sockfd, SOL_SOCKET, SO_SNDTIMEO, &timeout, len);  
        if (ret == -1) {  
            error = errno;  
            while ((close(sockfd) == -1) && (errno == EINTR));  
            errno = error;  
            return -1;  
        }  
          
        ret = connect(sockfd, (struct sockaddr*)&address, sizeof(address));  
        if (ret == -1) {  
            if (errno == EINPROGRESS) {  
                printf(“connecting timeout\n”);  
                return -1;  
            }  
              
            printf(“error occur when connecting to server\n”);  
            return -1;  
        }  
          
        char buffer[1024];  
        memset(buffer, ‘\0’, 1024);  
        ret = recv(sockfd, buffer, 1024, 0);  
          
        printf(“recv %d bytes, buf: %s\n”, ret, buffer);  
          
        return sockfd;  
    } 

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/160515.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • 服务降级的概念及应用手段

    服务降级的概念及应用手段什么是服务降级服务降级,就是对不怎么重要的服务进行低优先级的处理。说白了,就是尽可能的把系统资源让给优先级高的服务。资源有限,而请求是无限的。如果在并发高峰期,不做服务降级处理,一方面肯定会影响整体服务的性能,严重的话可能会导致宕机某些重要的服务不可用。所以,一般在高峰期,为了保证网站核心功能服务的可用性,都要对某些服务降级处理。服务降级手段拒绝服务判断应用来源,高峰时段拒

    2022年5月25日
    37
  • lammps教程:薄膜渗透模拟(3)–不同孔隙率对过滤效果的影响

    lammps教程:薄膜渗透模拟(3)–不同孔隙率对过滤效果的影响本文是薄膜渗透过滤的最后一篇文章:不同孔隙率薄膜建模。孔隙或空位缺陷的建模原理比较简单:删除一定数量的原子就可以。lammps自带delete_atoms可以随机删除一定比例的原子,如果对孔隙或空位的形状、尺寸等有特殊需求,需要用编程的方法删除原子。delete_atomsporosity命令可随时产生设定比例的原子,如删除50%的原子:delete_atomsporositymembrane0.5482793membrane为原子组0.5为删除原子的比例482793为随机数种子

    2022年9月3日
    1
  • (转)IIS中防盗链原理

    (转)IIS中防盗链原理转自:http://www.58tt.com/article/system/web/20091125/4882.html 首先笔者要说的是“盗链”很常见的现象,虽然没有采集那么“流行”,但是对于被盗者来说,碰到这事还真的及时解决,要不资源的消耗很可能会影响自身网站的正常运营。那究竟什么是盗链,怎样防止网站的信息被盗链呢?下面简单的说下:“盗链”的定义是:此内容不在自己服务器上,而通过技

    2022年7月23日
    4
  • 40个容易上瘾的HTML5网页游戏,总有一款适合你[通俗易懂]

    40个容易上瘾的HTML5网页游戏,总有一款适合你[通俗易懂]我记得姐姐家的孩子在刚刚才学会走路,说话还不能完整的时候就已经能自己用小手点出小游戏的网站来一个人自娱自乐。我一直在想这一代跟着计算机一起茁壮成长的孩子会不会也和美国那一代人一样,出现9岁的黑客和计算机天才。但是并不是信息的成长就能让教育同步。很多时候我们会发现教育在发展的大环境中并没有什么创新的思考。不管怎么说,我们还是需要小盆友们能有足够的想象力。不要被束缚!今天分享的是40个HTML5的网页

    2022年5月24日
    29
  • TranslateMessage函数 (转)「建议收藏」

    TranslateMessage函数 (转)「建议收藏」TranslateMessage是用来把虚拟键消息转换为字符消息。由于Windows对所有键盘编码都是采用虚拟键的定义,这样当按键按下时,并不得字符消息,需要键盘映射转换为字符的消息。TranslateMessage函数用于将虚…

    2022年9月13日
    0
  • 《前端运维》一、Linux基础–04Shell变量

    这一篇文章,我们就要开始学习正式的Shell语言部分的内容。那在开始之前,我们回忆一下,javascript语言,大体都包含了哪些内容?比如数据类型(对象、字符串、数值),数据结构(对象、数组)、运算

    2022年3月25日
    41

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号