[linux] socket 非阻塞模式使用注意事项

在使用 socket 的一些 api 的时候,默认情况下都是阻塞模式。比如使用 tcp socket 时,客户端调用 connect() 创建连接,connect() 返回的时候要么是创建连接成功了,要么是出现了错误,反正 connect() 返回的时候结果是确定的;tcp 服务端使用 accept() 接收连接的时候,accept() 返回的时候一般就是接收到了新的连接;使用 recv() 接收数据的时候,直到接收到数据之后,才会返回;使用 send() 发送数据的时候,所有数据都写到 tcp 缓冲区才会返回。以上就是这些 api 在阻塞模式下的工作原理。

在实际使用 socket 的时候,我们往往使用多路复用技术(select, poll, epoll)来监听 socket 中的事件,当监听到有事件到来时才会调用 accept() 或者 recv()。在这种情况下,因为有现成的事件,所以即使在阻塞模式,accept() 和 recv() 也能立即返回。对于 connect() 和 send(),我们一般是直接使用,而不是多路复用监听到有事件之后再调用。

在开发中,大部分情况下使用阻塞模式就已经足够了,但是在一些场景下,我们也需要使用 socket 的非阻塞模式。本文就记录一下上述 api 在非阻塞模式下使用的注意事项。

fd 默认是阻塞的,如果要设置为非阻塞模式,可以通过如下代码来完成。通过 fcntl 先获取 fd 的选项,然后再通过 fcntl 将非阻塞选项设置进去。

static int32_t set_fd_non_blocking(int32_t fd) {int opts = fcntl(fd, F_GETFL);if (opts < 0) {printf("fd[%d] GETFL failed[%s]", fd, strerror(errno));return -1;}if (fcntl(fd, F_SETFL, opts | O_NONBLOCK) < 0) {printf("fd[%d] SETFL failed[%s]", fd, strerror(errno));return -1;}return 0;
}

 对于 recv() 和 send() 来说,除了使用 fcntl 设置为非阻塞模式之外,还可以再 recv 和 send 的最后一个入参中设置 MSG_DONTWAIT 来将这次调用设置为非阻塞模式,两者效果是一样的。

1 connect

非阻塞模式下调用 connect,有以下 3 种情况需要分类处理:

(1)如果返回值是 0,说明连接已经建立成功

(2)返回 -1,并且错误码不是 EINPROGRESS,说明出现了错误,连接失败

(3)返回 -1,并且错误码是 EINPROGRESS,说明连接已经发起,正在处理中。这种情况下,就需要后续做监控来判断连接什么时候建立完成。

怎么监听正在处理的 connect 是不是已经完成,需要做如下两件事:

(1)使用多路复用技术(select, poll 或者 epoll) 监听套接字,监听写事件

(2)如果套接字有写事件,就通过 getsockopt 获取 socket 的 ERROR 状态,如果没有 ERROR,说明建立连接成功;如果有 ERROR,说明建立连接失败。使用多路复用技术监听的时候,也可以设置超时时间,超时监听不到,也说明建立连接失败。

获取 socket ERROR 状态的代码如下。

int error = 0;
if (getsockopt(fd, SOL_SOCKET, SO_ERROR, &error, &length) < 0) {return;
}
if (error != 0) {return;
}

2 accept

在实际使用中,一般会将服务端套接字加入到 epoll 中,使用 epoll 来监听,这样当服务端套接字有事件时,说明有新的连接到来,即使套接字处于阻塞状态,accept 也是不需要阻塞的。

如果监听套接字工作在非阻塞模式下,那么 accept 是否成功,和返回值和错误码有关系。

(1)返回值 > 0,说明接收到了新的连接,返回值就是新连接的 fd

(2)返回值是 -1,并且错误码不是 EAGAIN || EWOULDBLOCK || EINTR,说明出现了错误,获取失败

(3)返回值是 -1,并且错误码是 EAGAIN || EWOULDBLOCK || EINTR,说明需要重试

如下代码,创建一个 listening fd,然后将 fd 设置为非阻塞模式,这样在 accept 的时候,就会返回 EAGIN 错误。

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <errno.h>#define SERVER_IP     ("0.0.0.0")
#define SERVER_PORT   (12345)
#define MAX_LISTENQ   (32)static int32_t set_fd_non_blocking(int32_t fd) {int opts = fcntl(fd, F_GETFL);if (opts < 0) {printf("fd[%d] GETFL failed[%s]", fd, strerror(errno));return -1;}if (fcntl(fd, F_SETFL, opts | O_NONBLOCK) < 0) {printf("fd[%d] SETFL failed[%s]", fd, strerror(errno));return -1;}return 0;
}int main() {int ret = -1;int accept_fd = -1;int listen_fd = -1;struct sockaddr_in client_addr;struct sockaddr_in server_addr;socklen_t client = sizeof(struct sockaddr_in);listen_fd = socket(AF_INET, SOCK_STREAM, 0);if (listen_fd < 0) {printf("create socket error: %s\n", strerror(errno));return -1;}set_fd_non_blocking(listen_fd);memset(&server_addr, 0, sizeof(server_addr));server_addr.sin_family = AF_INET;server_addr.sin_addr.s_addr = inet_addr(SERVER_IP); /**< 0.0.0.0 all local ip */server_addr.sin_port = htons(SERVER_PORT);if (bind(listen_fd,(struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {perror("bind error: ");return -1;}if (listen(listen_fd, MAX_LISTENQ) < 0) {printf("listen error.\n");return -1;}while (1) {accept_fd = accept(listen_fd, (struct sockaddr*)&client_addr, &client);if(accept_fd < 0) {perror("accept error");}sleep(1);}return 0;
}

EAGIN 对应的字符串是 Resource temporarily unavailable。

3 recv

在阻塞模式下,recv 也不会等所有数据都到来之后才会返回,recv 是至少有收到一个字节便会返回。

仍然是根据返回值和错误码来判断:

(1)返回值 > 0,返回值表示读取的数据的字节数

(2)返回值为 -1,并且错误码是 EAGAIN || EWOULDBLOCK || EINTR 的话,说明当前没数据,需要重试;如果错误码不是 EAGAIN || EWOULDBLOCK || EINTR 的话,说明发生了错误

4 send

在阻塞模式下,当所有数据都发送完毕之后,send 才会返回。在非阻塞模式下,send 的情况要看返回值和错误码。

(1)返回值 > 0,表示发送的字节数

(2)返回值为 -1,并且错误码是 EAGAIN || EWOULDBLOCK || EINTR 的话,那么需要重试;如果错误码不属于 EAGAIN || EWOULDBLOCK || EINTR,那么说明发生了错误

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/740298.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Hadoop面经

hadoop原理 请说下 HDFS 的组织架构描述HDFS的读写流程HDFS 在读取文件的时候&#xff0c;如果其中一个块突然损坏了怎么办HDFS 在上传文件的时候&#xff0c;如果其中一个 DataNode 突然挂掉了怎么办 NameNode 在启动的时候会做哪些操作Secondary NameNode 了解吗&#xff0c;…

get命令使用提交代码

当你想要通过Git提交代码时&#xff0c;以下是一个详细的案例&#xff0c;包括从创建更改到推送到远程仓库的整个过程&#xff1a; 首先&#xff0c;确保你已经在本地仓库目录中进行了需要的更改。 添加更改到暂存区&#xff1a; git add . 这会将所有更改添加到Git的暂存区&…

微信小程序开发系列(二十九)·界面交互API·loading 提示框、showModal模态对话框、showToast消息提示框

目录 1. loading 提示框 1. 1 wx.showLoading()显示loading提示框 1.2 wx.hideLoading()关闭 loading 提示框 2. showModal 模态对话框 3. showToast 消息提示框 小程序提供了一些用于界面交互的 API&#xff0c;例如&#xff1a;loading 提示框、消息提示框、模态对…

高效的ElasticSearch Java API - my-elasticsearch-starter

ES Java Api有很多种&#xff0c;本文主要是基于Spring Boot进行封装的&#xff0c;为什么选择Spring Boot&#xff0c;可以看往期文章 Elasticsearch入门必读指南&#xff1a;到底选择哪个ES版本更合适 。 Spring Boot现在也是Java生态中主流的开发框架使用最广泛&#xff0c;…

信息系统项目管理师011:数字政府(1信息化发展—1.4数字中国—1.4.2数字政府)

文章目录 1.4.2 数字政府1.数字新特征2.主要内容 1.4.2 数字政府 信息技术的革新改变了人们传统的工作、学习、生活和娱乐方式&#xff0c;同时对政府提供信息服务&#xff0c;公民参与政府民主决策的方式提出了挑战。利用信息技术改进政府工作及服务的效率&#xff0c;形成新的…

python学习1:csv模块、time模块、random、jieba、worldcloud、pycharm的虚拟环境认识、black格式化文件

标准库与第三方库 模块&#xff08;modules&#xff09;&#xff1a;是包含python函数和变量的文件&#xff0c;名称符合Python标识符要求&#xff0c;并使用.py后缀 包&#xff08;package&#xff09;&#xff1a;是包含其他模块、包的文件夹。名称符合Python标识符要求&am…

安装配置HBase

HBase集群需要整个集群所有节点安装的HBase版本保持一致&#xff0c;并且拥有相同的配置&#xff0c;具体配置步骤如下&#xff1a; 1. 解压缩HBase的压缩包 2. 配置HBase的环境变量 3. 修改HBase的配置文件&#xff0c;HBase的配置文件存放在HBase安装目录下的conf中 4. 首…

蓝桥杯每日一题 走迷宫bfs 超超详细解释!!!

昨天学习了bfs的基本概念&#xff0c;今天来做一道经典习题练练手吧&#xff01; bfs常用的两类题型 1.从A出发是否存在到达B的路径(dfs也可) 2.从A出发到B的最短路径&#xff08;数小:<20才能用dfs&#xff09; 遗留的那个问题的答案- 题目&#xff1a;走迷宫 答案&…

Java爬虫-获取数据的方式之一

目录 一、jsoup的使用 1.概述 2.主要功能 3.快速入门 4.数据准备 二、Selenium 1.概述 2.使用 三、Selenium配合jsoup获取数据 四、爬虫准则 五、Seleniumjsoupmybatis实现数据保存 1.筛选需要的数据 2.创建一个表&#xff0c;准备存储数据 手写&#xff1f;不存在…

基于STM32的电动车防盗器设计

1.项目需求 点击遥控器 A 按键&#xff0c;系统进入警戒模式&#xff0c;一旦检测到震动&#xff08;小偷偷车&#xff09;&#xff0c;则喇叭发出声响报警&#xff0c; 吓退小偷。 点击遥控器 B 按键&#xff0c;系统退出警戒模式&#xff0c;再怎么摇晃系统都不会报警&#…

正则表达式中元字符的使用

// 转义字符(\ 特定的普通字符)&#xff0c;把这些特定的普通字符转义为具有特殊含义的字符&#xff0c; // \\字符(\ \) &#xff0c;把转义字符 \ 转义为普通的字符 \&#xff0c;正则表达式元字符中若含有\&#xff0c;所以用\\表示是元字符自己的含义&#xff0c;而不是…

【DevOps实战之k8s】使用Prometheus和Grafana监控K8S集群

【DevOps实战之k8s】使用Prometheus和Grafana监控K8S集群 目录 【DevOps实战之k8s】使用Prometheus和Grafana监控K8S集群系统架构Kubernetes集群指标抓取指标可视化警告PromQL示例按命名空间统计集群中的Pod数按命名空间重启Pod未就绪的PodCPU过度使用Memory过度使用健康的集群…

Qt/C++音视频开发69-保存监控pcm音频数据到mp4文件/监控录像/录像存储和回放/264/265/aac/pcm等

一、前言 用ffmpeg做音视频保存到mp4文件&#xff0c;都会遇到一个问题&#xff0c;尤其是在视频监控行业&#xff0c;就是监控摄像头设置的音频是PCM/G711A/G711U&#xff0c;解码后对应的格式是pcm_s16be/pcm_alaw/pcm_mulaw&#xff0c;将这个原始的音频流保存到mp4文件是会…

Java代码审计工程师直播第六期

本期直播课程将深入探讨Java代码审计的关键概念和技术。涵盖课题包括安全漏洞分析、代码审查方法、常见漏洞案例分析等。学员将通过实例掌握代码审计实战技能&#xff0c;提升对Java应用程序安全的认知和技能水平。 课程大小&#xff1a;6.1G 课程下载&#xff1a;https://do…

从根到叶:深入了解Map和Set

窗间映出一片高远的天空&#xff0c; 向晚的天际宁静而又清明。 我孤独的心灵在幸福地哭泣&#xff0c; 它在为天空如此美好而高兴。 恬静的晚霞一片火红&#xff0c; 晚霞灼烧着我的热情。 此刻的世界没有别人&#xff0c; 只有上帝&#xff0c;我和天空。 ——&#x…

SpringBlade error/list SQL 注入漏洞复现

0x01 产品简介 SpringBlade 是一个由商业级项目升级优化而来的 SpringCloud 分布式微服务架构、SpringBoot 单体式微服务架构并存的综合型项目。 0x02 漏洞概述 SpringBlade 框架后台 /api/blade-log/error/list路径存在SQL注入漏洞,攻击者除了可以利用 SQL 注入漏洞获取数…

让生活更加精致的APP?

晚上好&#xff0c;今天博主来介绍几款帮助你条理生活的APP&#xff0c;让你的生活更加精致&#xff0c;充满仪式感。 一&#xff0e;格志日记 一款以“格子”的方式记录日记的APP&#xff0c;非常简单明了&#xff0c;用户可以依据自己的喜好&#xff0c;来自由定义或者删除格…

SpringBoot实战项目——博客笔记项目

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、项目介绍二、项目的整体框架 2.1 数据库模块 2.2 前端模块 2.3 后端模块三、项目图片展示四、项目的实现 4.1 准备工作 4.…

【R语言实战】——金融时序分布拟合

&#x1f349;CSDN小墨&晓末:https://blog.csdn.net/jd1813346972 个人介绍: 研一&#xff5c;统计学&#xff5c;干货分享          擅长Python、Matlab、R等主流编程软件          累计十余项国家级比赛奖项&#xff0c;参与研究经费10w、40w级横向 文…

Android audiotrack尾帧无声

前言 产品一直有用户反馈音频截断问题。在机遇巧合下现学现卖音频知识处理相关问题。 问题描述 我们查看以下简化播放器代码&#xff1a; class AACPlayer(private val filePath: String) {private val TAG "AACPlayer"private var extractor: MediaExtractor? …