【计算机网络】网络编程接口 Socket API 解读(2)
Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。
本文讲述的 socket 内容源自 Linux 发行版 centos 9 上的 man 工具,和其他平台(比如 os-x 及不同版本会有些出入)。本文主要对各 API 进行详细介绍,从而更好的理解 socket 编程。
select
遵循 POSIX.1 - 2008
1.库
标准 c 库,libc, -lc
2.头文件
<sys/socket.h>
3.接口定义
int select(int nfds, fd_set *_Nullable restrict readfds,fd_set *_Nullable restrict writefds,fd_set *_Nullable restrict exceptfds,struct timeval *_Nullable restrict timeout);
4.接口描述
首先,我们需要注意 select 只能监听少于 FD_SETSIZE(1024) 个文件描述符,这在现在看来是非常不合理的,如果想不受这个限制,需要使用 poll 或者 epool。
select 可以同时监听多个文件描述符,只要有一个文件描述符有操作需求时即返回。文件描述符有操作需求指的是可以马上进行相关的 I/O 操作,比如 read 或者少量的写操作。
fd_set
一个表示一组文件描述符的结构体,根据 POSIX 要求,结构中最大文件描述符数量为 FD_SETSIZE。
File descriptor set
select() 接口重要的参数是 3 个文件描述符集合(以 fd_set 类型声明),这允许调用者在指定的文件描述符集合上等待 3 种类型的事件。每个 fd_set 参数都可以是 NULL,只要没有文件描述符集需要监听对应的事件。
值得注意的是,一旦接口返回,每个文件描述符集都被更新,来指示哪些文件描述符就绪了。因此,如果在一个循环中使用 select(),集合必须每次调用前重新初始化。
文件描述符集的内容可以使用以下宏来操作:
FD_ZERO()
这个宏用来清除集合中的所有文件描述符,是初始化文件描述符集的第一步。
FD_SET()
这个宏用来向集合中添加文件描述符,如果文件描述符已经存在,那么也不会报错,只是不进行任何操作。
FD_CLR()
这个宏用来从集合中移除指定文件描述符,如果文件描述符不存在,则不进行任何操作。
FD_ISSET()
select() 根据如下规则更新集合内容:select() 调用结束后,FD_ISSET() 宏用来检测指定文件描述符是否还位于集合中,如果存在则返回非 0 值,否则返回 0。
5.参数
(1)readfds
这个集合中的文件描述符用来监测其受否已经读就绪。一个文件描述读就绪指的是读操作不会阻塞,特别的是,EOF 也算是读就绪。
select() 函数返回后,readfds 中只会保留读就绪的文件描述符,其他都会被删除。
(2)writefds
这个集合中的文件描述符用来监测其受否已经写就绪。一个文件描述写就绪指的是写操作不会阻塞。不过即使一个文件描述符已经写就绪,但是大块的写操作可能也会阻塞。
select() 函数返回后,writefds 中只会保留写就绪的文件描述符,其他都会被删除。
(3)eceptfds
这个集合中的文件描述符用来监测其异常情况,一些异常情况的示例,在 poll() 的 POLLPRI 中会有讨论。
select() 返回后,exceptfds 中只保留发生异常情况的文件描述符。
(4)nfds
这个参数应该被设置为 3 个集合中文件描述符的最大值加 1。
(5)timeout
timeout 是一个 timeval 的结构,指定了 select() 等待文件描述符就绪的时间,这个接口会一直阻塞直到以下事件发生:
- 文件描述符就绪
- 调用被信号处理打断
- timeout 超时
值得注意的是,timeout 值会向上(rounded up)近似到系统时钟粒度,另外由于系统调度延迟,可能会导致阻塞间隔比 timeout 稍微大一些。
如果 timeout 的两个成员都为 0,那么 select 会立即返回(通常用于轮询)。
如果 timeout 是 NULL,select 会无限期等待直到有文件描述符就绪。
6.pselect()
pselect() 系统调用能够允许应用更安全的等待文件描述符就绪或者信号发生。
它和 select() 是一样的,除了以下几个地方:
- select() 使用 timeval 结构的 timeout,而 pselect() 使用 timespec 结构 的timeout
- select() 可能会更新 timeout 参数来指示还有多少剩余时间,而 pselect() 不会
- select() 没有信号屏蔽 sigmask 参数,相当于 pselect 的sigmask 参数为 NULL
sigmask 是一个指向信号屏蔽的指针。如果它不为空,那么 pselect() 首先会使用它代替当前的信号屏蔽,然后在进行 select(),最后再恢复原来的信号屏蔽。如果是 NULL,那么 pselect() 调用过程并不会改变信号屏蔽值。
除了时间精度上的差异,下面两端代码等效:
ready = pselect(nfds, &readfds, &writefds, &exceptfds,timeout, &sigmask);
sigset_t origmask;pthread_sigmask(SIG_SETMASK, &sigmask, &origmask);
ready = select(nfds, &readfds, &writefds, &exceptfds, timeout);
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
设计 pselect() 的原因是想要等待信号发生或者文件描述符就绪,那么就需要一个原子测试来解决数据竞争问题。比如,一个信号处理函数设置了一个标志并返回,如果信号刚好在测试的附近到达导致数据竞争时, select() 后面测试这个标志有可能无限期卡住。而 pselect() 允许先屏蔽信号,处理已经发生的信号,然后使用指定 sigmask 来调用 pselect() ,避免了数据竞争。
timeout
select() 的 timeout 结构体定义如下:
struct timeval {time_t tv_sec; /* seconds */suseconds_t tv_usec; /* microseconds */};
pselect() 对应的结构体时 timespec。
Linux 系统上 select() 会修改 timeout 值来反映未睡眠的时间,其他实现不是这么做的。POSIX.1 认为任何行为都是合法的。这就会导致 Linux 系统和其他系统之间的移植问题,所以,我们应该认为 timeout 在 select() 后是未知的值。
7.返回值
成功时,select() 和 pselect() 返回三个返回文件描述符集中的文件描述符总数(也就是 redfds、writefds、exceptfds 的中设置为 1 位数)。返回值可以为 0,表示在有文件描述符就绪前 timeout 超时。
发生错误时,返回 -1,并设置errno 来指示错误类型。文件描述符集并不会被修改,timeout 值是未定义的。
错误值定义如下:
EBADF | 集合中存在不合法的文件描述符,比如已经关闭的文件描述符或者发生错误的文件描述符),具体参见 BUGS |
EINTR | 捕获了一个信号,具体参见 signal(7) |
EINVAL | nfds 是负值,或者超过了 RLIMIT_NOFILE 资源限制,具体参见getrlimit(2) |
EINVAL | timeout 中的数值不合法 |
ENOMEM | 没有足够内存来分配内部表 |
在其他 UNIX 系统上,如果系统无法分配内核资源,select() 可能会返回 EAGAIN 错误而不是 ENOMEM。POSIX 为 poll() 定义了该错误,但是并没有为 select() 定义。考虑到程序的移植性,应该检查 EGAIN 并重新调用,就行 EINTR 处理一样。
8.注意
<sys/time.h> 也提供了 fd_set 的定义,fd_set 是一个固定大小的缓冲区,执行 FD_CLR 和 FD_SET 传入一个负值或者大于 FD_SETSIZE 的 fd 会导致不可预期的结果。此外,POSIX 要求 fd 是一个可用的文件描述符。
select() 和 pselect() 操作不受 O_NONBLOCK 标志的影响。
self-pipe 小技巧
在没有 pselect() 实现的系统上,可靠(更具有移植性)的信号捕捉可以通过 self-pipe 小技巧实现。这个技术在信号处理函数中向一个 pipe 中写入 1 字节,而该 pipe 的另一端由 select() 监听。为了防止满写阻塞和空读阻塞,pipe 的读写应采用非阻塞 I/O 方式。
模拟 usleep
在 usleep 出现前,一些代码使用 select() 来实现一种可移植的亚秒精度延迟,将所有集合设置为空,nfds 为 0,非空的 timeout值。
select() 和 poll() 间通知的映射
在 linux 代码树中,我们可以发现 select() 读、写、异常通知和 poll()/epoll() 事件通知之间的联系:
#define POLLIN_SET (EPOLLRDNORM | EPOLLRDBAND | EPOLLIN |EPOLLHUP | EPOLLERR)/* Ready for reading */#define POLLOUT_SET (EPOLLWRBAND | EPOLLWRNORM | EPOLLOUT |EPOLLERR)/* Ready for writing */#define POLLEX_SET (EPOLLPRI)/* Exceptional condition */
多线程应用
如果一个线程通过 select() 监听的文件描述符被另一个现场关闭,那么结果是未知的。在一些 UNIX 系统上,select() 会停止阻塞并返回,告知文件描述符就绪(后续操作会出错,除非刚好其他线程又打开了文件描述符并且就绪了)。在 Linux 及其他系统上,其他线程关闭文件描述符对 select() 没有任何影响。总结起来,应用如果依赖这些 具体的行为的话,就会产生 bug。
C 库和内核的差异
Linux 内核允许文件描述符集是任意大小的,由 nfds 的值来决定具体的大小。而 glibc 将fs_set 类型设置为固定值。参考 BUGS。
我们这里讲述的 pselect() 接口是 glibc 实现的,底层系统调用名字是 pselect6(),系统调用的行为和 pselect() 有些许不同。
Linux 的 pselect6() 系统调用修改 timeout 参数,然而 glibc 通过本地缓存 timeout 值隐藏了该行为。因此,glibc pselect6() 没有修改 timeout 参数,这也符合 POSIX.1-2001 要求。
pselect6() 系统调用的最后一个参数不是 sigset_t * 指针类型,而是如下格式:
struct {const kernel_sigset_t *ss; /* Pointer to signal set */size_t ss_len; /* Size (in bytes) of objectpointed to by 'ss' */};
这使得系统调用可以获取信号集指针及其大小,并考虑到大多数系统支持最大 6 个系统调用参数这个事实。关于信号处理的差异之处,可以参考 sigprocmask 的讨论。
glibc 历史细节
gblic 2.0 提供了 pselect() 的错误版本,它并没有 sigmask 参数。
glibc 2.1 到 2.2.1,为了获得 <sys/select.h> 中的 pselect() 声明,必须定义 _GNU_SOURCE 宏。
9.BUGS
POSIX 允许实现通过 FD_SETSIZE 来定义文件描述符集中文件描述符的上限,Linux 内核并没有限制,但是 glibc 实现将 fd_set 定为固定长度并将 FD_SETSIZE 设置为 1024,FD_*() 宏根据这个限制操作。为了能够监测多余 1023 个文件描述符,可以使用 poll() 或者 epoll。
fd_set 参数的输入输出属性是一个错误的设计,已经在 poll() 和 epoll() 改正过来。
根据 POSIX 要求,select() 应该检查所有集合中的文件描述符不能超过 nfds - 1,但是,当前实现会忽略掉那些文件描述符值大于当前进程打开的最大文件描述符值。根据 POSIX 要求,这些文件描述符会导致 EBADF 错误。
从 glibc 2.1 开始,glibc 使用 sigprocmask() 和 select() 实现了 pselect() 模拟,这个实现却遗留了 pselect() 解决的数据竞争问题。现在版本的 glibc 通常使用内核提供的不受数据竞争影响的 pselect() 系统调用。
Linux 上,select()可能报告 socket 文件描述符读就绪,但是后续的读却会阻塞,这个常发生在数据已达到但是数据的校验和不对,数据被丢弃。当然,也可能是误报。所以使用 O_NONBLOCK 的 sockets 更安全些。
Linux 上的 select() 会在被信号打断的情况下更新 timeout 值,POSIX.1 并不允许这样做。Linux 的 pselect() 是同样的行为,但是 glibc 隐藏了这种行为。
10.代码实例
#include <stdio.h>#include <stdlib.h>#include <sys/select.h>intmain(void){int retval;fd_set rfds;struct timeval tv;/* Watch stdin (fd 0) to see when it has input. */FD_ZERO(&rfds);FD_SET(0, &rfds);/* Wait up to five seconds. */tv.tv_sec = 5;tv.tv_usec = 0;retval = select(1, &rfds, NULL, NULL, &tv);/* Don't rely on the value of tv now! */if (retval == -1)perror("select()");else if (retval)printf("Data is available now.\n");/* FD_ISSET(0, &rfds) will be true. */elseprintf("No data within five seconds.\n");exit(EXIT_SUCCESS);}
相关文章:
【计算机网络】网络编程接口 Socket API 解读(2)
Socket 是网络协议栈暴露给编程人员的 API,相比复杂的计算机网络协议,API 对关键操作和配置数据进行了抽象,简化了程序编程。 本文讲述的 socket 内容源自 Linux 发行版 centos 9 上的 man 工具,和其他平台(比如 os-x …...
【黄啊码】PHP如何防止重复提交
防抖(Debounce)是一种防止重复提交的策略,它通过延迟一定时间来合并连续的操作,以确保只执行一次。 以下是几种防抖的实现方法以及对应的代码示例: 1. 前端 JavaScript 实现: 在前端使用 JavaScript 实现…...

2594. 修车的最少时间
文章目录 Tag题目来源题目解读解题思路方法一:二分枚举答案 写在最后 Tag 【二分枚举答案】【数组】 题目来源 2594. 修车的最少时间 题目解读 给你一个表示机械工能力的数组 ranks,ranks[i] 表示第 i 位机械工可以在 r a n k s [ i ] ∗ n 2 ranks[…...

vue 使用qrcode生成二维码并可下载保存
安装qrcode npm install qrcode --save代码 <template><div style"display: flex; flex-direction: column; align-items: center; justify-content center;"><div>查看溯源码,<a id"saveLink" style"text-decorati…...
网络融合的发展思路
虽然移动和固定网的融合代表了下一代网络的发展方向,但是目前移动和固定网的 发展还是独立的,有着各自的演进方式,要实现两个网络的完全融合是一个长期 的、逐步发展的过程。 网络融合的体系结构首先应坚持网络分层和功能分离的原则&#…...

报考浙江工业大学MBA项目如何选择合适的辅导班?
浙江工业大学MBA项目每年有数百人报考,在浙江省内除了浙大以外算是人数比较多的一个项目。2023级的招生中第一志愿也通过复试刷掉了百来人,在省内其实作为第一志愿报考的风险在逐渐增大,考生们如果坚持报考,则在针对联考初试的备考…...
算法训练第五十八天
总结:今日事单调栈的开端,还是挺巧妙的。 496. 下一个更大元素 I - 力扣(LeetCode) 代码: class Solution { public:vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& …...
如何快速生成一个H5滑动的卡片(单页和分页都有)
单页 <ul class"combo"><li v-for"(item, index) in arr" :key"index"><div class"combo-name">{{ item.A }}</div><div class"combo-price">{{ item.B }}</div><div class"co…...

嵌入式开发笔试面试
C语言部分: 1.gcc的四步编译过程 1.预处理 展开头文件,删除注释、空行等无用内容,替换宏定义。 gcc -E hello.c -o hello.i 2.编译 检查语法错误,如果有错则报错,没有错误则生成汇编文件。 gcc -S hello.i -o h…...

2023国赛数学建模B题思路分析 - 多波束测线问题
# 1 赛题 B 题 多波束测线问题 单波束测深是利用声波在水中的传播特性来测量水体深度的技术。声波在均匀介质中作匀 速直线传播, 在不同界面上产生反射, 利用这一原理,从测量船换能器垂直向海底发射声波信 号,并记录从声波发射到…...

thinkphp6 入门(5)-- 模型是什么 怎么用
一、模型 MVC架构 之前开发一个功能,后端为在控制器(C)中写 php SQL,前端为在页面(V)中写html css js,这就形成了 VC 架构。 但是发现,相同的数据逻辑(SQL…...

Hadoop HDFS 高阶优化方案
目录 一、短路本地读取:Short Circuit Local Reads 1.1 背景 1.2 老版本的设计实现 1.3 安全性改进版设计实现 1.4 短路本地读取配置 1.4.1 libhadoop.so 1.4.2 hdfs-site.xml 1.4.3 查看 Datanode 日志 二、HDFS Block 负载平衡器:Balan…...

通俗易懂讲解大模型:Tokenizer
Tokenizer Tokenizer 是 NLP pipeline 的核心组件之一。Tokenizer 的目标是:将文本转换为模型可以处理的数据。模型只能处理数字,因此 Tokenizer 需要将文本输入转换为数字输入。 通常而言有三种类型的 Tokenizer :Word-based Tokenizer、Cha…...

nested exception is java.io.FileNotFoundException
完整的错误信息: [main] ERROR o.s.boot.SpringApplication - Application run failed org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.heima.article.ArticleApplication]; nested exception is java…...

ARM编程模型-常用指令集
一、ARM指令集 ARM是RISC架构,所有的指令长度都是32位,并且大多数指令都在一个单周期内执行。主要特点:指令是条件执行的,内存访问使用Load/store架构。 二、Thumb 指令集 Thumb是一个16位的指令集,是ARM指令集的功能…...

MAC M2芯片执行yolov8 + deepsort 实现目标跟踪
MAC M2芯片执行yolov8 deepsort 实现目标跟踪 MAC M2 YoloX bytetrack实现目标跟踪 实验结果 MAC mps显存太小了跑不动 还是得用服务器跑 需要实验室的服务器跑 因为网上花钱跑4天太贵了!!! 步骤过程尝试: 执行mot17 数据集 …...
使用Python轻松实现文档编写
大家好,本文将介绍如何使用Python轻松实现文档编写,减少报告撰写的痛苦,使用Microsoft Word、python和python-docx库来简化报告撰写和从报告中提取信息。 案例 读取一个Word文档并进行编辑。 虽然听起来可能不那么令人振奋,但根…...
前后端分离项目,整合成jar包,刷新404或空白页,解决方法
问题解决 1、注销遇到404,或刷新遇到404 # 添加错误跳转 Component public class ErrorConfig implements ErrorPageRegistrar {Overridepublic void registerErrorPages(ErrorPageRegistry registry) {ErrorPage error404Page new ErrorPage(HttpStatus.NOT_FOU…...

前端、后端面试集锦
诸位读者,我们在工作的过程中,经常会因跳槽而面试。 你开发能力很强,懂得技术也很多,若加上条理清晰的面试话术,可以让您的面试事半功倍。 个人博客阅读量破170万,为尔倾心打造的 面试专栏-前端、后端面试…...

Web存储
目录 什么是 HTML5 Web 存储? 方法 cookie webStorage 会话存储 sessionStorage 本地存储localStorage 什么是 HTML5 Web 存储? 使用HTML5可以在本地存储用户的浏览数据。 早些时候,本地存储使用的是 cookie。但是Web 存储需要更加的安全与快速. 这些数据不会被保存在服…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...
基于大模型的 UI 自动化系统
基于大模型的 UI 自动化系统 下面是一个完整的 Python 系统,利用大模型实现智能 UI 自动化,结合计算机视觉和自然语言处理技术,实现"看屏操作"的能力。 系统架构设计 #mermaid-svg-2gn2GRvh5WCP2ktF {font-family:"trebuchet ms",verdana,arial,sans-…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

通过Wrangler CLI在worker中创建数据库和表
官方使用文档:Getting started Cloudflare D1 docs 创建数据库 在命令行中执行完成之后,会在本地和远程创建数据库: npx wranglerlatest d1 create prod-d1-tutorial 在cf中就可以看到数据库: 现在,您的Cloudfla…...

安宝特方案丨XRSOP人员作业标准化管理平台:AR智慧点检验收套件
在选煤厂、化工厂、钢铁厂等过程生产型企业,其生产设备的运行效率和非计划停机对工业制造效益有较大影响。 随着企业自动化和智能化建设的推进,需提前预防假检、错检、漏检,推动智慧生产运维系统数据的流动和现场赋能应用。同时,…...
java 实现excel文件转pdf | 无水印 | 无限制
文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【JVM】- 内存结构
引言 JVM:Java Virtual Machine 定义:Java虚拟机,Java二进制字节码的运行环境好处: 一次编写,到处运行自动内存管理,垃圾回收的功能数组下标越界检查(会抛异常,不会覆盖到其他代码…...
ffmpeg(四):滤镜命令
FFmpeg 的滤镜命令是用于音视频处理中的强大工具,可以完成剪裁、缩放、加水印、调色、合成、旋转、模糊、叠加字幕等复杂的操作。其核心语法格式一般如下: ffmpeg -i input.mp4 -vf "滤镜参数" output.mp4或者带音频滤镜: ffmpeg…...

PL0语法,分析器实现!
简介 PL/0 是一种简单的编程语言,通常用于教学编译原理。它的语法结构清晰,功能包括常量定义、变量声明、过程(子程序)定义以及基本的控制结构(如条件语句和循环语句)。 PL/0 语法规范 PL/0 是一种教学用的小型编程语言,由 Niklaus Wirth 设计,用于展示编译原理的核…...

EtherNet/IP转DeviceNet协议网关详解
一,设备主要功能 疆鸿智能JH-DVN-EIP本产品是自主研发的一款EtherNet/IP从站功能的通讯网关。该产品主要功能是连接DeviceNet总线和EtherNet/IP网络,本网关连接到EtherNet/IP总线中做为从站使用,连接到DeviceNet总线中做为从站使用。 在自动…...