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

19 篇文章 6 订阅 ¥29.90 ¥99.00

这几天发现一个现象,客户端正常连接服务器connect显然不会出现问题。

        在异常情况下,如果是服务器出现异常,connect能够立即返回失败;但是当客户端出现异常的情况下,分为两种情况:

        一种是不插网线,客户端没有获得ip地址,在这种情况下,connect也可以立即返回错误;

        二是但是当客户端插上网线,但是连接网络失败,也就是说能够获取到ip地址,但是和服务器是ping不通的。这种情况下connect就可能会发生阻塞,因为按照《UNIX 网络编程》中讲解,connect的在进行三次握手,如果失败情况,需要等待75s的超市时间的。

        我们主要讨论第二种情况如何解决,可以让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 的下面一行执行下去。
  1. sigset(SIGALRM, u_alarm_handler);:这行代码设置了一个信号处理函数u_alarm_handler,用于处理SIGALRM信号。当超时时间到达时,操作系统会发送SIGALRM信号给进程。

  2. alarm(2);:这行代码设置一个定时器,即在2秒钟后发送SIGALRM信号给进程。这意味着在2秒内,如果连接操作没有完成,就会超时。

  3. code = connect(socket_fd, (struct sockaddr*)&socket_st, sizeof(struct sockaddr_in));:这是连接操作的代码,用于建立与服务器的连接。根据之前的讨论,connect函数在第二次握手时返回结果。

  4. alarm(0);:这行代码将定时器取消,即停止发送SIGALRM信号。

  5. sigrelse(SIGALRM);:这行代码解除对SIGALRM信号的阻塞,即恢复原本的信号处理方式。

其中,处理函数可以如下定义,也可以加入更多的错误处理。
void u_alarm_handler()
{
}

connect调用发起第一次握手,在第二次握手返回结果,accept在第三次握手后返回。

第一次握手时,创建连接,connect处于阻塞状态,等待服务器accept,第二次握手成功,connect从阻塞返回,accept同样的需要收到客户端ack以后才从阻塞返回,连接建立完成

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值