当前位置: 首页 > news >正文

connect的非阻塞模式

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

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

主要步骤大致如下:

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

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

  /*** 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.创建一个socketint 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 动作被信号中断,重试connectstd::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;elsestd::cout << "connect to server error." << std::endl;//5. 关闭socketclose(clientfd);return 0;}

相关文章:

  • Discourse 如何通过终端工具访问 PGSQL
  • 多模态
  • Android APP 音视频(02)MediaProjection录屏与MediaCodec编码
  • java找不到符号解决办法
  • 《Programming from the Ground Up》阅读笔记:p75-p87
  • css更改图片颜色
  • ReadAgent,一款具有要点记忆的人工智能阅读代理
  • Vue3点击按钮实现跳转页面并携带参数
  • openFeign配置okhttp
  • 63.利用PEB获取模块列表
  • Hive小文件合并
  • DDoS 究竟在攻击什么?
  • 每日任务:TCP/IP模型和OSI模型的区别
  • VsCode | 让空文件夹始终展开不折叠
  • 算法与算法分析
  • JavaScript 如何正确处理 Unicode 编码问题!
  • 2018天猫双11|这就是阿里云!不止有新技术,更有温暖的社会力量
  • canvas绘制圆角头像
  • Hibernate【inverse和cascade属性】知识要点
  • isset在php5.6-和php7.0+的一些差异
  • JavaScript 是如何工作的:WebRTC 和对等网络的机制!
  • Java-详解HashMap
  • Java小白进阶笔记(3)-初级面向对象
  • LeetCode刷题——29. Divide Two Integers(Part 1靠自己)
  • LeetCode算法系列_0891_子序列宽度之和
  • python3 使用 asyncio 代替线程
  • Quartz初级教程
  • React-生命周期杂记
  • vue的全局变量和全局拦截请求器
  • 高性能JavaScript阅读简记(三)
  • 如何合理的规划jvm性能调优
  • 异常机制详解
  • 在electron中实现跨域请求,无需更改服务器端设置
  • Spark2.4.0源码分析之WorldCount 默认shuffling并行度为200(九) ...
  • 阿里云移动端播放器高级功能介绍
  • # .NET Framework中使用命名管道进行进程间通信
  • # 再次尝试 连接失败_无线WiFi无法连接到网络怎么办【解决方法】
  • #我与Java虚拟机的故事#连载04:一本让自己没面子的书
  • (003)SlickEdit Unity的补全
  • (13)DroneCAN 适配器节点(一)
  • (3)选择元素——(17)练习(Exercises)
  • (solr系列:一)使用tomcat部署solr服务
  • (八)Docker网络跨主机通讯vxlan和vlan
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (二)七种元启发算法(DBO、LO、SWO、COA、LSO、KOA、GRO)求解无人机路径规划MATLAB
  • (附源码)ssm航空客运订票系统 毕业设计 141612
  • (四)进入MySQL 【事务】
  • (四)图像的%2线性拉伸
  • (一)为什么要选择C++
  • (转)iOS字体
  • (转)JVM内存分配 -Xms128m -Xmx512m -XX:PermSize=128m -XX:MaxPermSize=512m
  • (转)甲方乙方——赵民谈找工作
  • (转载)CentOS查看系统信息|CentOS查看命令
  • .gitignore
  • .Net CF下精确的计时器