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

news2024/11/26 10:23:00

在使用 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.coloradmin.cn/o/1511699.html

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

相关文章

Unsupervised RL:METRA: Scalable Unsupervised RL with Metric-Aware Abstraction

ICLR 2024 Oral paper Intro 无监督RL旨在发现潜在的行为帮助提高下游任务效率以往方法集中于探索以及基于互信息的技能发现(skill)。然而去前者在高危复杂空间实现困难&#xff0c;后者也容易因为缺乏激励导致探索能力不足。本文提出METRA核心观点认为与其在复杂状态空间处理…

OGNL表达式

文章目录 一、简介二、快速入门三、详细使用3.1 基本数据类型3.2 对象类型3.3 List集合3.4 Set集合3.5 Map集合3.6 数组3.7 静态调用3.8 算术运算3.9 逻辑运算3.10 同时执行多个表达式3.11 位运算 Ognl使用总结 - 源链接在此 >> 一、简介 OGNL&#xff08;Object-Graph…

小兔鲜鲜项目(前端vue3)

成果图 大家喜欢给一个赞被&#xff0c; 项目地址&#xff1a;gitee 注意&#xff1a;项目克隆下去之后先运行 npm i之后安装项目插件包之后在npm run dev 运行就可以了

“成像光谱遥感技术中的AI革命:ChatGPT应用指

遥感技术主要通过卫星和飞机从远处观察和测量我们的环境&#xff0c;是理解和监测地球物理、化学和生物系统的基石。ChatGPT是由OpenAI开发的最先进的语言模型&#xff0c;在理解和生成人类语言方面表现出了非凡的能力。本文重点介绍ChatGPT在遥感中的应用&#xff0c;人工智能…

python读取execl里的图片

正常的读取图片 from openpyxl import load_workbook from PIL import Imagefrom openpyxl import load_workbook wb load_workbook(rC:\Users\Administrator\Downloads\output1111.xlsx) ws wb[wb.sheetnames[0]] for image in ws._images:data image.anchor._fromif image…

深入学习默认成员函数——c++指南

前言&#xff1a;类和对象是面向对象语言的重要概念。 c身为一门既面向过程&#xff0c;又面向对象的语言。 想要学习c&#xff0c; 首先同样要先了解类和对象。 本节就类和对象的几种构造函数相关内容进行深入的解析。 目录 类和对象的基本概念 封装 类域和类体 访问限定符…

家长应如何培养孩子对人工智能(AI)的兴趣?无际Ai分享

随着科技的飞速发展&#xff0c;人工智能已经成为了当今社会的重要组成部分。然而&#xff0c;在中小学阶段&#xff0c;很少有学校系统地对学生进行人工智能方面的教育。作为普通家庭的家长&#xff0c;我们可以通过一些方法来激发孩子对人工智能的兴趣&#xff0c;让他们在这…

网络工程师——2024自学

一、怎样从零开始学习网络工程师 当今社会&#xff0c;人人离不开网络。整个IT互联网行业&#xff0c;最好入门的&#xff0c;网络工程师算是一个了。 什么是网络工程师呢&#xff0c;简单来说&#xff0c;就是互联网从设计、建设到运行和维护&#xff0c;都需要网络工程师来…

第7讲:数组和函数实践:扫雷游戏

第7讲&#xff1a;数组和函数实践&#xff1a;扫雷游戏 1. 扫雷游戏分析和设计1.1 扫雷游戏的功能说明1.2 游戏的分析和设计1.2.1 数据结构的分析1.2.2 文件结构设计 2. 扫雷游戏的代码实现3. 扫雷游戏的扩展 1. 扫雷游戏分析和设计 1.1 扫雷游戏的功能说明 • 使用控制台实现…

Edu 12 --- Simple Subset -- 题解 (一个比较巧妙的思维算法题)

Simple Subset&#xff1a; 题解&#xff1a; 思路解析&#xff1a; 题目要求任意两个数的和为质数&#xff0c;那我们最坏情况就是任意选择一个数&#xff0c;此时子集为最大。 如果子集中有两个奇数或者偶数&#xff0c;他们两个之和一定会被2整除&#xff0c;那么我们只能…

keilC51明明已经定义的变量却报错未定义与期待asm

文章目录 keil_C51采用的是C89标准&#xff1a;C89变量定义标准&#xff1a;程序块&#xff1a; expected __asm&#xff1a;已经定义某个变量却报错未定义该变量&#xff1a;从汇编语言中看&#xff1a; keil_C51采用的是C89标准&#xff1a; C89变量定义标准&#xff1a; 1…

Web框架开发-HTTP协议

一、HTTP协议简介 HTTP协议是Hyper Text Transfer Protocol&#xff08;超文本传输协议&#xff09;的缩写,是用于万维网&#xff08;WWW:World Wide Web &#xff09;服务器与本地浏览器之间传输超文本的传送协议。 HTTP是一个属于应用层的面向对象的协议&#xff0c;由于其简…

C while 循环

只要给定的条件为真&#xff0c;C 语言中的 while 循环语句会重复执行一个目标语句。 语法 C 语言中 while 循环的语法&#xff1a; while(condition) {statement(s); }在这里&#xff0c;statement(s) 可以是一个单独的语句&#xff0c;也可以是几个语句组成的代码块。 co…

【C++进阶】C++多态概念详解

C多态概念详解 一&#xff0c;多态概念二&#xff0c;多态的定义2.1 多态构成的条件2.2 什么是虚函数2.3 虚函数的重写2.3.1 虚函数重写的特例2.3.2 override和final 2.4 重载和重写&#xff08;覆盖&#xff09;和重定义&#xff08;隐藏&#xff09;的区别 三&#xff0c;抽象…

数据库规范化设计案例解析

1.介绍 数据库规范化设计是数据库设计的一种重要方法&#xff0c;旨在减少数据库中的冗余数据&#xff0c;提高数据的一致性&#xff0c;确保数据依赖合理&#xff0c;从而提高数据库的结构清晰度和维护效率。规范化设计通过应用一系列的规范化规则&#xff08;或称“范式”&a…

高边开关驱动容性负载

Hello UU们,有做汽车电子的吗? 如果做汽车电子可能会用到很多高边开关,高边开关带的负载是让容性负载,或者是感性负载时候会比较恶劣,容性负载可能一下子不容易带起来.因为电池和负载电容上的巨大压差,高边开关上流过的电流非常之大,为此我们可以使用缓慢的打开高边开关,高边…

Firefly: 一站式大模型训练工具

Firefly: 一站式大模型训练工具 转载自GitHub项目&#xff1a;https://github.com/yangjianxin1/Firefly 项目简介 Firefly 是一个开源的大模型训练项目&#xff0c;支持对主流的大模型进行预训练、指令微调和DPO&#xff0c;包括但不限于Gemma、Qwen1.5、MiniCPM、Llama、I…

关于并发编程的一些总结

并发编程 1.synchronized是什么&#xff1f; synchronized是Java中的一个关键字&#xff0c;主要是为了解决多个线程访问共享资源的同步性&#xff0c;可以保证被它修饰的代码块或方法在任何时间至多只有一个线程执行。 2.synchronized的进化史? 在早期Java版本中&#xf…

Python合并两张图片 | 先叠透明度再合并 (附Demo)

目录 前言正文 前言 用在深度学习可增加噪音&#xff0c;增加数据集等 推荐阅读&#xff1a;Pytorch 图像增强 实现翻转裁剪色调等 附代码&#xff08;全&#xff09; 正文 使用Pillow库来处理图像&#xff08;以下两张图来自网络&#xff09; 图一&#xff1a; 图二&…

飞塔防火墙开局百篇——002.FortiGate上网配置——透明模式配置(Transparent)

透明模式配置 开启透明模式创建策略 在不改变现有网络拓扑前提下&#xff0c;将防火墙NGFW以透明模式部署到网络中&#xff0c;放在路由器和交换机之间&#xff0c;防火墙为透明模式&#xff0c;对内网网段192.168.1.0/24的上网进行4~7层的安全防护。 登陆FortiGate防火墙界面&…