connect的非阻塞模式

本文参考:connect 函数在阻塞和非阻塞模式下的行为

一般情况下,在使用connect连接服务端时,需要等待一会儿才会函数才会返回,导致程序阻塞。为了降低阻塞的影响,我们可能会单独开个线程处理connect请求,例如在界面当中,就会启用一个线程,避免UI卡死。

当然,这里还有一些其他的方法,也就是我们常见的非阻塞IO。

主要步骤大致如下:

  1. 创建socket,并将 socket 设置成非阻塞模式;
  2. 调用 connect 函数,此时无论 connect 函数是否连接成功会立即返回;如果返回-1并不表示连接出错,如果此时错误码是EINPROGRESS
  3. 接着调用 select 函数,在指定的时间内判断该 socket 是否可写,如果可写说明连接成功,反之则认为连接失败。

按上述流程编写代码如下:

c 复制代码
  /**
   * Linux 下正确的异步的connect写法,linux_nonblocking_connect.cpp
   * zhangyl 2018.12.17
   */
  #include <sys/types.h> 
  #include <sys/socket.h>
  #include <arpa/inet.h>
  #include <unistd.h>
  #include <iostream>
  #include <string.h>
  #include <stdio.h>
  #include <fcntl.h>
  #include <errno.h>

  #define SERVER_ADDRESS "127.0.0.1"
  #define SERVER_PORT     3000
  #define SEND_DATA       "helloworld"

  int main(int argc, char* argv[])
  {
      //1.创建一个socket
      int clientfd = socket(AF_INET, SOCK_STREAM, 0);
      if (clientfd == -1)
      {
          std::cout << "create client socket error." << std::endl;
          return -1;
      }

      //连接成功以后,我们再将 clientfd 设置成非阻塞模式,
      //不能在创建时就设置,这样会影响到 connect 函数的行为
      int oldSocketFlag = fcntl(clientfd, F_GETFL, 0);
      int newSocketFlag = oldSocketFlag | O_NONBLOCK;
      if (fcntl(clientfd, F_SETFL,  newSocketFlag) == -1)
      {
          close(clientfd);
          std::cout << "set socket to nonblock error." << std::endl;
          return -1;
      }

      //2.连接服务器
      struct sockaddr_in serveraddr;
      serveraddr.sin_family = AF_INET;
      serveraddr.sin_addr.s_addr = inet_addr(SERVER_ADDRESS);
      serveraddr.sin_port = htons(SERVER_PORT);
      for (;;)
      {
          int ret = connect(clientfd, (struct sockaddr *)&serveraddr, sizeof(serveraddr));
          if (ret == 0)
          {
              std::cout << "connect to server successfully." << std::endl;
              close(clientfd);
              return 0;
          } 
          else if (ret == -1) 
          {
              if (errno == EINTR)
              {
                  //connect 动作被信号中断,重试connect
                  std::cout << "connecting interruptted by signal, try again." << std::endl;
                  continue;
              } else if (errno == EINPROGRESS)
              {
                  //连接正在尝试中
                  break;
              } else {
                  //真的出错了,
                  close(clientfd);
                  return -1;
              }
          }
      }

      fd_set writeset;
      FD_ZERO(&writeset);
      FD_SET(clientfd, &writeset);
      //可以利用tv_sec和tv_usec做更小精度的超时控制
      struct timeval tv;
      tv.tv_sec = 3;  
      tv.tv_usec = 0;
      if (select(clientfd + 1, NULL, &writeset, NULL, &tv) != 1)
      {
          std::cout << "[select] connect to server error." << std::endl;
          close(clientfd);
          return -1;
      }

      int err;
      socklen_t len = static_cast<socklen_t>(sizeof err);
      if (::getsockopt(clientfd, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
      {
          close(clientfd);
          return -1;
      }

      if (err == 0)
          std::cout << "connect to server successfully." << std::endl;
      else
          std::cout << "connect to server error." << std::endl;

      //5. 关闭socket
      close(clientfd);

      return 0;
  }
相关推荐
ShyanZh32 分钟前
【Python3基础】19-Socket 与 TCP、UDP 编程
python·网络协议·tcp/ip·udp
汤姆yu2 小时前
Gemini 4 Argon模型综述:技术、能力与实际使用指南
网络·安全·web安全·ai·大模型
Seraphina362 小时前
SQL注入(二)盲注
网络·数据库·经验分享·笔记·sql
2401_868534783 小时前
无线网络规划设计
服务器·网络
螺蛳粉 螺蛳粉3 小时前
第二篇:keepalived VRRP 原理篇:协议与 VIP 漂移机制深入解析
网络·智能路由器·负载均衡·keepalived·高可用
盟接之桥3 小时前
AI助手:你的7×24小时智能管家——让异常预警无处不在
大数据·网络·数据库·人工智能·制造·ai编程
2603_969579285 小时前
内网数据自动备份工具:隔离网络下的数据保护方案
网络
逗脑IDE5 小时前
零基础学ESP32:RFID无线射频卡——让ESP32拥有“刷卡“能力!
开发语言·网络·人工智能·python·esp32·硬件开发
cws2004015 小时前
监控摄像头选型指南
运维·网络·监控
和裕6 小时前
定制纸箱刀模费全解析:费用定义与可减免合作场景
大数据·运维·网络·人工智能·算法