Linux C select 的学习

一. select 系统调用

1. 函数说明

#include <sys/select.h>
#include <sys/time.h>int select(int nfds, fd_set *readset, fd_set *writeset, fd_set *exceptset,struct timeval *timeout);
  • nfds: 是一个整数值,是指集合中所有文件描述符的范围,即所有文件描述符的最大值加 1。在linux系统中,select 的默认最大值为1024 。设置这个值的目的是为了不用每次都去轮询这1024fd,假设我们只需要几个套接字,我们就可以用最大的那个套接字的值加上1作为这个参数的值,当我们在等待是否有套接字准备就绪时,只需要监测nfds+1个套接字就可以了,这样可以减少轮询时间以及系统的开销。

  • readfds: 用来检测输入是否准备就绪的文件描述符集合(从内核读,用的最多)

  • writefds: 用来检测输出是否准备就绪的文件描述符集合(往内核写,比较少用)

  • exceptset; 很少用到,我没用过。用来检测异常情况是否发生的文件描述符集合。

  • timeout: 堵塞的超时时间,如果为 NULL 则堵塞。

系统调用 select() 会一直堵塞,直到一个或者多个文件描述符集合称为就绪态,select 就会往下执行。

套接字可写的条件

这个概念很常见,跟套接字可读一样。

  1. 使用 select 监听,如果是可读套接字的 fd_set,那么套接字对应的内核中,一旦收到数据,那么就会使 select 返回,fd_set准备就绪。
  2. 同样道理,使用 select 监听,如果是可写的套接字的 fd_set,那么套接字对应的内核中,一旦数据的缓冲区有空余的位置写数据,那么就会使 select 返回,fd_set准备就绪。这句话有些难理解。

具体的说:

一、 满足下列四个条件中的任何一个时,一个套接字准备好读。

  1. 该套接字接收缓冲区中的数据字节数大于等于套接字接收缓存区低水位。 对于TCP和UDP套接字而言,缓冲区低水位的值默认为1。那就意味着,默认情况下,只要缓冲区中有数据,那就是可读的。我们可以通过使用SO_RCVLOWAT套接字选项(参见setsockopt函数)来设置该套接字的低水位大小。此种描述符就绪(可读)的情况下,当我们使用read/recv等对该套接字执行读操作的时候,套接字不会阻塞,而是成功返回一个大于0的值(即可读数据的大小)。
  2. 该连接的读半部关闭(也就是接收了FIN的TCP连接)。对这样的套接字的读操作,将不会阻塞,而是返回0(也就是EOF)。
  3. 该套接字是一个listen的监听套接字,并且目前已经完成的连接数不为0。对这样的套接字进行accept操作通常不会阻塞。
  4. 有一个错误套接字待处理。对这样的套接字的读操作将不阻塞并返回-1(也就是返回了一个错误),同时把errno设置成确切的错误条件。这些待处理错误(pending error)也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。

二、满足下列四个条件中的任何一个时,一个套接字准备好写。

  1. 该套接字发送缓冲区中的可用空间字节数大于等于套接字发送缓存区低水位标记时,并且该套接字已经成功连接(UDP套接字不需要连接)。对于TCP和UDP而言,这个低水位的值默认为2048,而套接字默认的发送缓冲区大小是8k,这就意味着一般一个套接字连接成功后,就是处于可写状态的。我们可以通过SO_SNDLOWAT套接字选项(参见setsockopt函数)来设置这个低水位。此种情况下,我们设置该套接字为非阻塞,对该套接字进行写操作(如write,send等),将不阻塞,并返回一个正值(例如由传输层接受的字节数,即发送的数据大小)。
  2. 该连接的写半部关闭(主动发送FIN包的TCP连接)。对这样的套接字的写操作将会产生SIGPIPE信号。所以我们的网络程序基本都要自定义处理SIGPIPE信号。因为SIGPIPE信号的默认处理方式是程序退出。
  3. 使用非阻塞的connect套接字已建立连接,或者connect已经以失败告终。即connect有结果了。
  4. 有一个错误的套接字待处理。对这样的套接字的写操作将不阻塞并返回-1(也就是返回了一个错误),同时把errno设置成确切的错误条件。这些待处理的错误也可以通过指定SO_ERROR套接字选项调用getsockopt获取并清除。

在这里插入图片描述

  • 后面有一个例子,是关于一个readfdsfd_set 是一个标准输入,一个 writefdsfd_set 是标准输出。使用 select 堵塞等待。
  • 当键盘有输入时 readfds 中是标准输出, select 会立马返回就绪
  • 当监听writefds时,因为里面放的是标准输入,因为标准输入在正常情况下就是可写的,所以 select 会立马返回并就绪,不会堵塞。

2. 数据类型 fd_set

这个网上资料很多,细节很多。我有空回来再补

操作 fd_set 的四个宏:

#include <sys/select.h>   int FD_ZERO(int fd, fd_set *fdset);    // 初始化:相当于将 fd_set 清零
int FD_CLR(int fd, fd_set *fdset);     // 将文件描述符 fd 从 fd_set 中移除
int FD_SET(int fd, fd_set *fd_set);    // 将文件描述符 fd 加入 fd_set 中
int FD_ISSET(int fd, fd_set *fdset);   // 如果文件描述符 fd 是 fd_set 所指向的成员,返回 true

3. timeout 参数

  • 类型: struct timeval
#include <sys/time.h>struct timeval{long tv_sec;   /*秒 */long tv_usec;  /*微秒 */   }

上面说,timeoutNULL 时,select 会堵塞。如果 timeout 的两个值都为 0,不会堵塞。select直接立即返回

4. select 返回值

这个看多了用多了代码就会了,大部分使用select 的代码都会出现判断它的所有返回值的:

while(1)
{...ret = select(nfds, readfds, ...);if (ret > 0) {}else if (ret == 0) {}else if (ret < 0) {}...
}

返回值:

  • 大于 0:返回一个或多个已经就绪的文件描述符
  • 小于 0: 有错误发生:一般是 EBADFEINTR 。具体上网搜
  • 等于 0: 超时,每个文件描述符集合会清空。

5. example

"t_select.h" 在文末获取。

#include <sys/time.h>
#include <sys/select.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "t_select.h"/*** example:*      ./t_select 10 0r 1w* * comment:*      10: timeout(second)*      0r: 以读形式读取标准输入*      1w: 以写形式去写标准输出
*/int main(int argc, char *argv[])
{fd_set readfds, writefds;int ready, nfds, fd, numRead, j;struct timeval timeout;struct timeval *pto;char buf[10]; /* Large enough to hold "rw\0" */if (argc < 2 || strcmp(argv[1], "--help") == 0)usageError(argv[0]);/* Timeout for select() is specified in argv[1] */// "-": 代表堵塞; 否则就是超时时间if (strcmp(argv[1], "-") == 0){pto = NULL; /* Infinite timeout */}else{pto = &timeout;timeout.tv_sec = getLong(argv[1], 0, "timeout");timeout.tv_usec = 0; /* No microseconds */}/* Process remaining arguments to build file descriptor sets */nfds = 0;FD_ZERO(&readfds);FD_ZERO(&writefds);for (j = 2; j < argc; j++){numRead = sscanf(argv[j], "%d%2[rw]", &fd, buf);if (numRead != 2)usageError(argv[0]);if (fd >= FD_SETSIZE)printf("file descriptor exceeds limit (%d)\n", FD_SETSIZE);if (fd >= nfds)nfds = fd + 1; /* Record maximum fd + 1 */if (strchr(buf, 'r') != NULL)FD_SET(fd, &readfds);if (strchr(buf, 'w') != NULL)FD_SET(fd, &writefds);}/* We've built all of the arguments; now call select() */// 第一个参数:套接字返回ready = select(nfds, &readfds, &writefds, NULL, pto);/* Ignore exceptional events */if (ready == -1)usageError("select");/* Display results of select() */printf("ready = %d\n", ready);for (fd = 0; fd < nfds; fd++)printf("%d: %s%s\n", fd, FD_ISSET(fd, &readfds) ? "r" : "",FD_ISSET(fd, &writefds) ? "w" : "");if (pto != NULL)printf("timeout after select(): %ld.%03ld\n",(long)timeout.tv_sec, (long)timeout.tv_usec / 1000);exit(EXIT_SUCCESS);
}

文末

/**************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>#define GET_NUM_H#define GN_NONNEG 01 /* Value must be >= 0 */
#define GN_GT_0 02   /* Value must be > 0 *//* By default, integers are decimal */
#define GN_ANY_BASE 0100 /* Can use any base - like strtol(3) */
#define GN_BASE_8 0200   /* Value is expressed in octal */
#define GN_BASE_16 0400  /* Value is expressed in hexadecimal *//* Print a diagnostic message that contains a function name ('fname'),the value of a command-line argument ('arg'), the name of thatcommand-line argument ('name'), and a diagnostic error message ('msg'). */
static void
gnFail(const char *fname, const char *msg, const char *arg, const char *name)
{fprintf(stderr, "%s error", fname);if (name != NULL)fprintf(stderr, " (in %s)", name);fprintf(stderr, ": %s\n", msg);if (arg != NULL && *arg != '\0')fprintf(stderr, "        offending text: %s\n", arg);exit(EXIT_FAILURE);
}
/* Convert a numeric command-line argument ('arg') into a long integer,returned as the function result. 'flags' is a bit mask of flags controllinghow the conversion is done and what diagnostic checks are performed on thenumeric result; see get_num.h for details.'fname' is the name of our caller, and 'name' is the name associated withthe command-line argument 'arg'. 'fname' and 'name' are used to print adiagnostic message in case an error is detected when processing 'arg'. */static long
getNum(const char *fname, const char *arg, int flags, const char *name)
{long res;char *endptr;int base;if (arg == NULL || *arg == '\0')gnFail(fname, "null or empty string", arg, name);base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8: (flags & GN_BASE_16)  ? 16: 10;errno = 0;res = strtol(arg, &endptr, base);if (errno != 0)gnFail(fname, "strtol() failed", arg, name);if (*endptr != '\0')gnFail(fname, "nonnumeric characters", arg, name);if ((flags & GN_NONNEG) && res < 0)gnFail(fname, "negative value not allowed", arg, name);if ((flags & GN_GT_0) && res <= 0)gnFail(fname, "value must be > 0", arg, name);return res;
}
/* Convert a numeric command-line argument string to a long integer. See thecomments for getNum() for a description of the arguments to this function. */long getLong(const char *arg, int flags, const char *name)
{return getNum("getLong", arg, flags, name);
}
/* Convert a numeric command-line argument string to an integer. See thecomments for getNum() for a description of the arguments to this function. */int getInt(const char *arg, int flags, const char *name)
{long res;res = getNum("getInt", arg, flags, name);if (res > INT_MAX || res < INT_MIN)gnFail("getInt", "integer out of range", arg, name);return res;
}
/***********************************************************************************************************/static void usageError(const char *progName)
{fprintf(stderr, "Usage: %s {timeout|-} fd-num[rw]...\n", progName);fprintf(stderr, "      - means infinite timeout; \n");fprintf(stderr, "      r = monitor for read\n");fprintf(stderr, "      w = monitor for write\n\n");fprintf(stderr, "      e.g.: %s - 0rw 1w\n", progName);exit(EXIT_FAILURE);
}

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

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

相关文章

SpringBoot 接口 字节数组直接显示为图片

源码&#xff1a; import java.io.ByteArrayOutputStream; import javax.imageio.ImageIO; import org.springframework.web.bind.annotation.RequestMapping;/*** 获取二维码图像* 二维码支付** param price 金额* return 二维码图像* throws IOException IOException*/ Requ…

Windows Nginx 服务器部署(保姆级)

大家好 我是寸铁 不知道怎么部署Windows Nginx 服务器看过来 手把手带你部署服务器 将你的本地网页部署到服务器上 话不多说&#xff0c;直接上操作&#xff01;&#xff01;&#xff01; Windows Nginx服务器部署 进入下载地址&#xff1a; http://nginx.org/en/download.h…

stm32学习笔记:中断的应用:对射式红外传感器计次旋转编码器计次

相关API介绍 EXT配置API(stm32f10x exti.h&#xff09; NVIC 配置API (misc.h) 初始化的中断的步骤 第一步&#xff1a;配置RCC时钟&#xff0c;把涉及外设的时钟都打开 第二步&#xff1a;配置GPIO&#xff0c;设置为输入模式 第三步&#xff1a;配置AFIO&#xff0…

VsCode同时编译多个C文件

VsCode默认只能编译单个C文件&#xff0c;想要编译多个文件&#xff0c;需要额外进行配置 第一种方法 ——> 通过手动指定要编译的文件 g -g .\C文件1 .\C文件2 -o 编译后exe名称 例如我将demo.c和extern.c同时编译得到haha.exe g -g .\demo.c .\extern.c -o haha 第二种…

移动应用-Android开发基础\核心知识点

Android开发基础 知识点 1 介绍了解2 系统体系架构3 四大应用组件4 移动操作系统优缺点5 开发工具6 配置工具7 下载相关资源8JDK下载安装流程9配置好SDK和JDK环境10 第一个Hello word11 AS开发前常用设置12模拟器使用运行13 真机调试14 AndroidUI基础布局15 加载展示XML布局16…

dll动态链接库及ocx activex 控件regsvr32注册失败 解决方法(Win10)

一、错误提示说明&#xff1a; 1、regsvr32注册dll或ocx activex控件时提示模块 dll加载失败&#xff0c;请确保该二进制存储在指定的路径中&#xff0c;或者调试它以检查该二进制或相关的.dll文件是否有问题”。 检查了.dll文件路径也没有问题&#xff0c;在开发机器上是可以…

【Java 进阶篇】JavaScript三元运算符详解

JavaScript是一门广泛用于前端和后端开发的编程语言&#xff0c;具备强大的表达式和运算符。本篇博客将重点介绍JavaScript中的三元运算符&#xff0c;解释其语法、用法和示例。如果您是JavaScript初学者&#xff0c;或者希望更深入了解这门语言的运算符&#xff0c;那么这篇博…

适合自学的网络安全基础技能“蓝宝书”:《CTF那些事儿》

CTF比赛是快速提升网络安全实战技能的重要途径&#xff0c;已成为各个行业选拔网络安全人才的通用方法。但是&#xff0c;本书作者在从事CTF培训的过程中&#xff0c;发现存在几个突出的问题&#xff1a; 1&#xff09;线下CTF比赛培训中存在严重的 “最后一公里”问题 &#…

鉴源实验室 | AUTOSAR SecOC:保障汽车通信的安全

作者 | 沈平 上海控安可信软件创新研究院汽车网络安全组 来源 | 鉴源实验室 社群 | 添加微信号“TICPShanghai”加入“上海控安51fusa安全社区” 在现代汽车行业中&#xff0c;随着电子控制单元&#xff08;ECUs&#xff09;的普及以及车与车之间通信的不断增加&#xff0c;确…

【Linux 安装Kibana 及 Es 分词器安装】

一、客户端Kibana安装 Kibana是一个开源分析和可视化平台&#xff0c;旨在与Elasticsearch协同工作。参考文档 1. 下载并解压缩Kibana 下载路径 选择的版本是和 ElasticSearch 对应&#xff08;7.17.3&#xff09; 下载后上传到Linux 系统中&#xff0c;并放在 /root/ 下&a…

利达卓越:关注环保事业,持续赋能科技

随着全球环境问题的日益突出,绿色金融作为一种新兴的金融模式逐渐受到各国的重视。绿色金融是指在金融活动中,通过资金、信贷和风险管理等手段,支持环境友好和可持续发展的项目和产业。绿色金融的出现是为了应对气候变化、资源短缺、污染问题等现实挑战,促进经济的绿色转型和可…

Unity可视化Shader工具ASE介绍——6、通过例子说明ASE节点的连接方式

大家好&#xff0c;我是阿赵。继续介绍Unity可视化Shader编辑插件ASE的用法。上一篇已经介绍了很多ASE常用的节点。这一篇通过几个小例子&#xff0c;来看看这些节点是怎样连接使用的。   这篇的内容可能会比较长&#xff0c;最终是做了一个遮挡X光的效果&#xff0c;不过把这…

Kotlin函数作为参数指向不同逻辑

Kotlin函数作为参数指向不同逻辑 fun sum(): (Int, Int) -> Int {return { a, b -> (a b) } }fun multiplication(): (Int, Int) -> Int {return { a, b -> (a * b) } }fun main(args: Array<String>) {var math: (Int, Int) -> Intmath sum()println(m…

Django 静态自定义化配置

STATIC # APP本地静态资源目录&#xff08;就APP对应的&#xff09; STATIC_URL "/static/"# 远程静态文件URL&#xff08;少用&#xff09; REMOTE_STATIC_URL# 外部引用静态文件目录&#xff08;外层的&#xff09; STATICFILES_DIRS [os.path.join(BASE_DIR, &…

Vue-2.8插槽

插槽分为默认插槽&#xff08;组件内定制一处结构&#xff09;、具名插槽&#xff08;组件内定制多处结构&#xff09; 作用域插槽不属于以上&#xff0c;只是插槽的一个传参语法 默认插槽 作用&#xff1a;让组件内部的一些结构支持自定义 需求&#xff1a;要在页面中显示…

安卓 kotlin-supportFragmentManager报红

如果你继承baseActivity 请查看 是不是继承 AppCompatActivity

ceph 分布式存储与部署

目录 一、存储基础&#xff1a; 1.单机存储设备&#xff1a; 2. 单机存储的问题&#xff1a; 3. 商业存储解决方案&#xff1a; 4. 分布式存储&#xff1a; 5. 分布式存储的类型&#xff1a; 二、Ceph 简介&#xff1a; 三、Ceph 优势&#xff1a; 四、Ceph 架构&#xff1a…

阿里云ModelScope 是一个“模型即服务”(MaaS)平台

简介 项目地址&#xff1a;https://github.com/modelscope/modelscope/tree/master ModelScope 是一个“模型即服务”(MaaS)平台&#xff0c;旨在汇集来自AI社区的最先进的机器学习模型&#xff0c;并简化在实际应用中使用AI模型的流程。ModelScope库使开发人员能够通过丰富的…

Tomcat和HPPT协议

1.介绍 1.Java EE 规范 JavaEE&#xff08;java Enterprise Edition&#xff09;&#xff1a;java企业版 JavaEE 规范是很多的java开发技术的总称。这些技术规范都是沿用自J2EE的。一共包括了13个技术规范 2.WEB概述 WEB在计算机领域中代表的是网络 像我们之前所用的WWW&…

[GXYCTF2019]Ping Ping Ping - RCE(空格、关键字绕过[3种方式])

[GXYCTF2019]Ping Ping Ping 1 解题流程1.1 小试牛刀1.2 三种解法1.2.1 解法一:变量定义拼接绕过1.2.2 解法二:base64编码绕过1.2.3 解法三:内联执行绕过2 思考总结1 解题流程 1.1 小试牛刀 1、提示?ip,结合题目名称,我们直接输入?ip=127.0.0.1 PING 127.0.0.1 (127.…