connect函数会阻塞,怎么解决?

建立socket后默认connect()函数为阻塞连接状态,在大多数实现中,connect的超时时间在75s至几分钟之间,想要缩短超时时间,可解决问题的两种方法:

  • 方法一、将socket句柄设置为非阻塞状态。
  • 方法二、采用信号处理函数设置阻塞超时控制。

##设置套接字为非阻塞
可以将套接字设置为非阻塞,下面是具体步骤

  1. 建立socket
  2. 将该socket设置为非阻塞模式
  3. 调用connect(),如果返回0,则连接建立;如果返回-1,检查errno ,如果值为 EINPROGRESS,则连接正在建立。
  4. 使用select()检查该socket描述符是否可写(注意,是可写)
  5. 根据select()返回的结果判断connect()结果,如果规定时间内成功建立,则描述符变为可写;否则,采用getsockopt函数捕获错误信息
  6. 将socket设置为阻塞模式(如果你的程序不需要用阻塞模式的,这步就省了,不过一般情况下都是用阻塞模式的,这样也容易管理

下面是设置套接字为非阻塞的例程

{
    int sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(sockfd < 0) exit(1);
    struct sockaddr_in serv_addr;
    ………//以服务器地址填充结构serv_addr
    int error=-1, len;
    len = sizeof(int);
    timeval tm;
    fd_set set;
    unsigned long ul = 1;

    ioctl(sockfd, FIONBIO, &ul); //设置为非阻塞模式
    bool ret = false;

    if( connect(sockfd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) == -1)
    {
            tm.tv_set = TIME_OUT_TIME;
            tm.tv_uset = 0;
            FD_ZERO(&set);
            FD_SET(sockfd, &set);
            if( select(sockfd+1, NULL, &set, NULL, &tm) > 0)
            {
                    getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, (socklen_t *)&len);
                    if(error == 0) ret = true;
                        else ret = false;
            } 
            else 
                ret = false;
    }
    else 
        ret = true;
    ul = 0;
    ioctl(sockfd, FIONBIO, &ul); //设置为阻塞模式
    //下面还可以进行发包收包操作
    ……………
}

##采用信号处理函数设置阻塞超时控制

sigset(SIGALRM, u_alarm_handler);
alarm(2);
code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));
alarm(0);
sigrelse(SIGALRM);

首先定义一个中断信号处理函数u_alarm_handler,用于超时后的报警处理,然后定义一个2秒的定时器,执行connect,当系统connect成功,则系统正常执行下去;如果connect不成功阻塞在这里,则超过定义的2秒后,系统会产生一个信号,触发执行u_alarm_handler函数, 当执行完u_alarm_handler后,程序将继续从connect的下面一行执行下去。

其中,处理函数可以如下定义,也可以加入更多的错误处理。

void u_alarm_handler()
{
}