[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,一经查实,立即删除!

相关文章

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

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

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;再怎么摇晃系统都不会报警&#…

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? …

TortoiseSVN 报错:The server unexpectedly closed the connetion

前言 CentOS7Linux 安装subversionmod_dav_svn&#xff0c;搭建subversion(svn)服务器 The server unexpectedly closed the connetion 解决办法 重启Apache服务 shell> systemctl restart httpd

uniapp—day02

个人名片&#xff1a; &#x1f60a;作者简介&#xff1a;一名大二在校生 &#x1f921; 个人主页&#xff1a;坠入暮云间x &#x1f43c;座右铭&#xff1a;给自己一个梦想&#xff0c;给世界一个惊喜。 &#x1f385;**学习目标: 坚持每一次的学习打卡 文章目录 WXML 和HTML区…

系统及其分类

系统定义 系统&#xff1a;指若干相互关联的事物组合而成的具有特定功能的整体。 系统的基本作用&#xff1a;对输入信号进行加工和处理&#xff0c;将其转换为所需要的输出信号。 系统分类 系统的分类错综复杂&#xff0c;主要考虑其数学模型的差异来划分不同类型。主要分为…

使用reprepro+nginx搭建apt服务器

目录 项目背景 项目要求 项目开发过程 1、apt服务器的搭建 2、实现自定义指定源文件列表来实现apt update更新 3、实现软件启动时自动更新 4. source.list中镜像源地址的格式 项目开发的难点/坑点 总结 项目背景 前面写过一篇“利用Nginx搭建一个apt服务器”&#xff…

Midjourney绘图欣赏系列(十)

Midjourney介绍 Midjourney 是生成式人工智能的一个很好的例子&#xff0c;它根据文本提示创建图像。它与 Dall-E 和 Stable Diffusion 一起成为最流行的 AI 艺术创作工具之一。与竞争对手不同&#xff0c;Midjourney 是自筹资金且闭源的&#xff0c;因此确切了解其幕后内容尚不…

vue项目:webpack打包优化实践

本文目录 一、项目基本信息二、分析当前项目情况1、使用 webpack-bundle-analyzer 插件2、使用 speed-measure-webpack-plugin 插件 三、解决构建问题1、caniuse-lite 提示的问题2、 warning 问题 四、打包速度优化1、修改source map2、处理 loader 五、webpack性能优化1、使用…