zoukankan      html  css  js  c++  java
  • 使用select控制非阻塞connect()超时的示例

    #include <stdio.h>

    #include <sys/time.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/socket.h>
    #include <arpa/inet.h>
    #include <stdlib.h>
    #include <string.h>
    #include <errno.h>
    #include <fcntl.h>

    static int tac_connect_server(int fd, struct sockaddr *addr, int dstlen, int timeout)
    {
    int ret = -1, err_num = 0, flags = 0;
    struct timeval wr_time = {0,0};
    fd_set write_set;
    fd_set read_set;
    int rc = 0, err_value = 0, err_len = sizeof(err_value);

    flags = fcntl(fd, F_GETFL, 0);

    /*设置非阻塞模式*/
    fcntl(fd, F_SETFL, flags|O_NONBLOCK);
    ret = connect(fd, addr, dstlen);
    printf("LINE:%d,connect ret:%d ", __LINE__, ret);
    if(0 != ret)
    {
    err_num = errno;
    if(EINPROGRESS == err_num)
    {
    FD_ZERO(&write_set);
    FD_ZERO(&read_set);
    FD_SET(fd, &write_set);
    FD_SET(fd, &read_set);
    wr_time.tv_sec = timeout;
    ret = select(fd+1, &read_set, &write_set, NULL, &wr_time);
    err_num = errno;
    printf("LINE:%d,select ret:%d ", __LINE__,ret);
    if(0 >= ret)
    {
    printf("LINE:%d, connect fail,errno[%d]:%s ", __LINE__, err_num, strerror(err_num));
    ret = -1;
    }
    else if(1 == ret && FD_ISSET(fd, &write_set))
    {
    printf("LINE:%d, connect sucess ", __LINE__);
    ret = 0;
    }
    else if(2 == ret && FD_ISSET(fd, &write_set) && FD_ISSET(fd, &read_set))
    {
    /*getsockopt是必须的,因为1:在套接字连接成功可读、可写时返回2,并且读写被置位;2:套接字出错时同样会返回2,并且读写被置位。为区分1、2两种情况需调用getsockopt判断套接字是否出错。*/
    rc = getsockopt(fd, SOL_SOCKET, SO_ERROR, &err_value, &err_len);
    /*同时判断返回值rc和错误码err_value原因:查看资料getsockopt目前有两种实现:1:如果发生错误,getsockopt在err_value中返回错误,但getsockopt本身返回值为0(Berkeley实现);2:getsockopt返回-1,并在err_value中返回错误(Solaris实现)。基于此只需判断err_value,但为安全考虑两个同时判断*/
    if(0 == rc && 0 == err_value)
    {
    printf("LINE:%d, connect sucess ", __LINE__);
    ret = 0;
    }
    else
    {
    printf("LINE:%d, connect fail,errno[%d]:%s ", __LINE__, err_num, strerror(err_num));
    ret = -1;
    }
    }
    else
    {
    printf("LINE:%d, connect fail,errno[%d]:%s ", __LINE__, err_num, strerror(err_num));
    ret = -1;
    }
    }
    else
    {
    printf("LINE:%d, connect fail,errno[%d]:%s ", __LINE__, err_num, strerror(err_num));
    ret = -1;
    }
    }
    else
    {
    printf("LINE:%d, connect sucess ", __LINE__);
    ret = 0;
    }
    printf("LINE:%d, connect finish:ret:%d, fd:%d ", __LINE__, ret, fd);
    fcntl(fd, F_SETFL, flags);
    return ret;
    }

    static int tac_connect(char * dst, int port, int timeout)
    {
    int sockfd = 0;
    struct sockaddr_in s;

    printf("dst:%s, port:%d, timeout:%d ", dst, port, timeout);
    if (port==0) port=49;

    if ((sockfd = socket(AF_INET, SOCK_STREAM, 0)) < 0)
    //if ((sockfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
    {
    printf("LINE:%d, create socket fail ", __LINE__);
    return -1;
    }
    #if 0
    s.sin_addr.s_addr = htonl(INADDR_ANY);
    s.sin_family = AF_INET;
    s.sin_port = 0;
    if (bind(sockfd, (struct sockaddr *)&s, sizeof(s)) < 0)
    {
    printf("LINE:%d, bind socket fail ", __LINE__);
    close(sockfd);
    return -1;
    }
    #endif
    if ((s.sin_addr.s_addr = inet_addr(dst)) == 0xffffff)
    {
    printf("LINE:%d, inet_addr fail ", __LINE__);
    close(sockfd);
    return -1;
    }
    s.sin_family = AF_INET;
    s.sin_port = htons(port);
    if (tac_connect_server(sockfd, (struct sockaddr *)&s, sizeof(s), timeout) < 0) {
    printf("LINE:%d, tac_connect_server fail ", __LINE__);
    close(sockfd);
    return -1;
    }
    printf("LINE:%d,connect sucess ", __LINE__);
    close(sockfd);
    return 0;
    }

    int main(int argc, char** argv)
    {
    int i=0;

    if(argc < 4 )
    {
    printf("usag:test <dstip> <port> <timeout> ");
    return -1;
    }
    for(i=0; i<4; i++)
    {
    if(tac_connect(argv[1], atoi(argv[2]), atoi(argv[3])) != 0)
    {
    printf("============================================ ");
    printf("LINE:%d, trytimes:%d ", __LINE__, i);
    printf("============================================ ");
    continue;
    }
    else
    {
    printf("++++++++++++++++++++++++++++++++++++++++++ ");
    printf("connect sucess, trytimes:%d ", i);
    printf("++++++++++++++++++++++++++++++++++++++++++ ");
    break;
    }
    }

    return 0;
    }

    备注:

    getsockopt处也可使用以下方式判断是否真正建立连接成功:
    1、调用read,读取长度为0字节的数据.如果read调用失败,则表示连接建立失败,而且read返回的errno指明了连接失败的原因.如果连接建立成功,read应该返回0;
    2、再调用一次connect.它应该失败,如果错误errno是EISCONN,就表示套接口已经建立,而且第一次连接是成功的;否则,连接就是失败的;

  • 相关阅读:
    POJ NOI0113-05 素数回文数的个数(PKU2928)
    POJ NOI0113-04 垂直直方图(PKU2800)
    POJ NOI0113-03 八进制小数(PKU2765)
    UVALive2245 POJ1131 HDU1376 ZOJ1086 Octal Fractions【进制】
    HDU2708 Vertical Histogram
    POJ2136 Vertical Histogram【打印图案】
    POJ2190 HDU2714 ISBN
    POJ NOI0113-02 不吉利日期(PKU2723)
    POJ NOI0113-01 数制转换(PKU2710)
    POJ NOI MATH-7828 最大公约数与最小公倍数
  • 原文地址:https://www.cnblogs.com/wangliangblog/p/13262344.html
Copyright © 2011-2022 走看看