【I/O多路复用】阻塞非阻塞I/O

embedded/2024/10/10 20:06:43/

IO复用的本质是不在任何地方阻塞!

总结:非阻塞情况下,要配合循环使用。

阻塞&非阻塞IO

  • 阻塞:在进/线程中,发起一个调用时,在调用返回之前,进/线程会被阻塞等待,等待中的进/线让出CPU的使用权。

  • 非阻塞:在进/线程中,发起一个调用时,会立即返回。

  • 会阻塞的四个函数:connect()、accept()、send()、recv()。

connect函数看起好像不会阻塞,但是会经历三次握手是阻塞的,如果connect一个不存在的地址,10s后会返回失败,说明是阻塞的。

阻塞&非阻塞IO的应用场景

  • 在传统的网络服务端程序中(每连接每线/进程),采用阻塞IO。

  • 在I0复用的模型中,事件循环不能被阻塞在任何环节,所以,应该采用非阻塞IO

事件循环是不能阻塞的,如果阻塞了就不能响应其他的客户端请求了!上一节epoll服务端代码的send函数是存在问题的,如果发送数据量足够大,或者网络带宽不够,那么send就会阻塞,无法响应其他客户端的请求。

1.非阻塞lO-connect()

  • 对非阻塞的IO调用connect()函数,不管是否能连接成功,connect()都会立即返回失败,设置错误代码errno==EINPROGRESS。 需要仔细思考...有点绕的,这个特殊,跟后面三个不一致。

    if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0){if (errno!=EINPROGRESS) // 说明失败{printf("connect(%s:%s) failed.\n",argv[1],argv[2]); close(sockfd);  return -1;}}

代码是为了理解。

  • 判断是否连接成功:对非阻塞的IO调用connect()函数后,如果socket的状态是可写的,证明连接是成功的,否则是失败的。

在程序中使用IO复用的函数处理一下,使用poll(因为更简单)监视socket的写事件,如果是可写的说明是成功的,如果是不可写就是失败的。

client1.cpp中部分代码:

//把socket设置成非阻塞。
int setnonblocking(int fd)
{int flags;if((flags = fcntl(fd,F_GETFL,0))==-1)flags=0;
​return fcntl(fd,F_SETFL,flags|O_NONBLOCK);    
}
int main(int argc, char *argv[])
{int sockfd;struct sockaddr_in servaddr;
​if ((sockfd=socket(AF_INET,SOCK_STREAM,0))<0) { printf("socket() failed.\n"); return -1; }setnonblocking(sockfd);
​memset(&servaddr,0,sizeof(servaddr));servaddr.sin_family=AF_INET;servaddr.sin_port=htons(atoi(argv[2]));servaddr.sin_addr.s_addr=inet_addr(argv[1]);
​if (connect(sockfd, (struct sockaddr *)&servaddr,sizeof(servaddr)) != 0){//非阻塞的socket的connect都会返回失败,errno设置为EINPROGRESS。if(errno!=EINPROGRESS){//errno不等于EINPROGRESS一定失败,等于不一定成功,也可能会失败。printf("connect(%s:%s) failed.\n",argv[1],argv[2]); close(sockfd);  return -1;}}
​pollfd fds;fds.fd = sockfd;fds.events = POLLOUT;poll(&fds,1,-1);if(fds.revents==POLLOUT)printf("connect ok.\n");elseprintf("connect faild.\n");
}

客户端采用,服务端很少使用,知道就行了,面试的时候问到了能回答即可。

2.非阻塞IO-accept()

  • 对非阻塞的IO调用accept(),如果已连接队列中没有socket,函数立即返回失败,errno==EAGAIN;如果已连接队列中有socket,accept返回socket。需要仔细思考...有点绕的。

tcpepoll1.cpp部分代码:

// 初始化服务端的监听端口。
int initserver(int port);
​
//把socket设置成非阻塞。
int setnonblocking(int fd)
{int flags;if((flags = fcntl(fd,F_GETFL,0))==-1)flags=0;return fcntl(fd,F_SETFL,flags|O_NONBLOCK);    
}
​
int main(int argc,char *argv[])
{if (argc != 2) { printf("usage: ./tcpepoll port\n"); return -1; }
​// 初始化服务端用于监听的socket。int listensock = initserver(atoi(argv[1]));printf("listensock=%d\n",listensock);
​setnonblocking(listensock);
​while(true){if(accept(listensock,0,0)==-1){if(errno!=EAGAIN){perror("accept:");return -1;}}elsebreak;}printf("客户端已连接。\n");return 0;
}

真实项目中不会这么写,以上代码为了测试,看一下就行,理解非阻塞。

3.非阻塞IO-recv()

对非阻塞的IO调用recv(),如果没数据可读函数(接收缓冲区为空)立即返回失败,errno==EAGAIN。

🔴重点:读取不到数据的时候返回-1;客户端断开的时候读取到0!别搞反了。

4.非阻塞lO-send()

对非阻塞的I0调用send(),如果socket不可写(发送缓冲区已满),函数立即返回失败,errno==EAGAIN。


http://www.ppmy.cn/embedded/125512.html

相关文章

网络中串接模式的使用

除了防火墙&#xff0c;以下设备也可以使用串接模式&#xff1a; 一、入侵检测系统&#xff08;IDS&#xff09;和入侵防御系统&#xff08;IPS&#xff09; 1. 工作原理 - IDS通过监测网络流量&#xff0c;识别潜在的恶意活动&#xff0c;如攻击、入侵和异常行为。它通常以被…

我们是如何将Docker构建时间缩短40%的

by: WL Mapmost从设计之初&#xff0c;便选择了云原生道路&#xff0c;在软件开发过程中自然也少不了容器化技术的使用。当然&#xff0c;我们也为Mapmost产品中使用的所有组件构建了 docker 镜像。然而&#xff0c;随着时间的推移&#xff0c;其中一些镜像变得越来越大&#…

LeetCode 347.前 K 个高频元素

题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2] 示例 2: 输入: nums [1], k 1 输出: [1] 提示&#xff1a; 1 < nums.length &…

简易登录注册(表单校验与登录登出);测试类;postman测试;

项目是如何创建的&#xff0c;最简易的登陆注册功能是怎么实现的&#xff0c;数据库不能明文存放密码&#xff0c;密码经过了怎么样的处理存入数据库 前端使用nodejs18 后端项目需要等待maven加载完相关依赖&#xff0c;后端使用java17 1后端 1.1 创建项目所需要的数据库 内…

文件夹作为普通文件而非子模块管理

relaxed_ik_ros2 文件夹下存在 .gitmodules 文件和 .gitignore 文件。这说明该目录已经被 Git 识别为子模块。 要将这个文件夹作为普通文件而非子模块管理&#xff0c;你可以按以下步骤操作&#xff1a; 1. 删除子模块配置 首先删除 .gitmodules 文件中的子模块配置。你可以…

Midjourney零基础学习

Midjourney学习笔记TOP05 Midjourney的各种参数设置 Midjourney的用户操作界面没有醒目的工具栏、属性栏&#xff0c;所有的操作都是通过调用各种指令和参数进行的。 【Niji version】 这里只讲解&#xff1a;V4 和 V5 两种模型 Niji V4的这个模型版本主要是用来生成动画和插…

【WebGIS】基础知识及工具介绍

文章目录 介绍基础知识1、地图服务规范2、地理坐标、世界坐标、屏幕坐标。3、坐标系分为两类4、基于 WGS84 测量系统的坐标系为两种5、常见的坐标系6、常见的地图使用的坐标系7、常见的 gis 库默认坐标系使用工具[桌面端软件]1、ArcView、ESRI ArcGIS Desktop与ArcGIS Pro等。2…

《C++代码热更新:为高效开发注入新活力》

一、引言 在软件开发的过程中&#xff0c;我们常常面临着这样的挑战&#xff1a;当程序已经部署到生产环境后&#xff0c;发现了一些需要紧急修复的 bug 或者需要添加新的功能。传统的方法是停止程序运行&#xff0c;进行代码修改&#xff0c;然后重新编译、部署&#xff0c;这…