select系统调用(实现I/O复用)

news2024/12/23 22:57:23

API

在一段指定时间内,监听用户感兴趣的文件描述符上的可读、可写、异常事件。

int select(int nfds, fd_set *readfds, fd_set *writefds,
           fd_set *exceptfds, struct timeval *timeout);

文件描述符集合fd_set

是一个用于管理文件描述符集合的结构体。select调用返回时,内核将修改fd_set通知应用程序哪些文件描述符已就绪。

typedef struct {
    unsigned long fds_bits[FD_SETSIZE / (8 * sizeof(unsigned long))];
} fd_set;
fds_bits

一个数组,用于存储文件描述符的位图,每个文件描述符状态(可读、可写、异常)在位图中用一位表示。这样可以使用少量的存储空间来表示多个文件描述符的状态。

sizeof(unsigned long)返回unsigned long类型在当前系统上的字节大小。通常在 32 位系统上为 4 字节,而在 64 位系统上为 8 字节。

因为1字节有8 位,所以8*sizeof(unsigned long)表示一个unsigned long能表示的位数。

FD_SETSIZE

一个常量,定义了fd_set能容纳的最大文件描述符的数量,这限制了select能同时处理的文件描述符总量。它的值通常是1024,但可以在不同的实现中有所不同。

例子

假设FD_SETSIZE是1024,且unsigned long是 8 字节,则8*sizeof(unsigned long)=64,1024/64=16,这意味着fd_set中需要16个unsigned long来表示 1024 个文件描述符的状态。

常用的宏

位操作比较繁琐,可以用宏访问fd_set结构体中的位。

// FD_ZERO: 初始化 fd_set 结构,将指定的集合 set 清空。
FD_ZERO(fd_set *set);

// FD_SET: 将文件描述符 fd 添加到 fd_set 结构 set 中。
FD_SET(int fd, fd_set *set);

// FD_CLR: 从 fd_set 结构 set 中移除文件描述符 fd。
FD_CLR(int fd, fd_set *set);

// FD_ISSET: 检查文件描述符 fd 是否在 fd_set 结构 set 中。
FD_ISSET(int fd, fd_set *set);

参数

nfds

监视的文件描述符数量,通常是最高文件描述符的值加一。例如,如果你监视的文件描述符是 0, 1, 和 2,则nfds应为 3。

readfds

指向一个fd_set结构的指针,用于指定需要监视可读事件的文件描述符集合。

writefds

指向一个fd_set结构的指针,用于指定需要监视可写事件的文件描述符集合。

exceptfds

指向一个fd_set结构的指针,用于指定需要监视异常条件的文件描述符集合。异常条件通常包括紧急数据等。

timeout

一个timeval结构的指针,用于设置select函数的超时时间。采用指针参数,是因为内核将修改它以告诉程序select等待了多久。如果设置为NULL,则表示无限等待;如果指定了时间,则select将在超时后返回。

struct timeval {
    long tv_sec;    // 秒数
    long v_usec;    // 微秒数
};

返回值

成功

返回就绪(可读、可写和异常)的文件描述符数量。

超时

在超时时间内没有任何文件描述符就绪,返回0。

失败

返回-1,并设置errno为EINTR,可以通过perror函数输出错误信息。

文件操作符就绪条件

socket可读条件

接收到数据

对端发送了数据,数据会被放入socket的接收缓冲区。一旦接收缓冲区中有数据可供读取,socket 的状态会变为可读,可以调用读取函数来获取数据。

对端关闭连接

对端调用了关闭操作,本端socket仍然可读,但读取的数据量为 0,表示连接已经正常关闭,而没有任何剩余数据可供读取。这是一个正常的情况,通常可以通过检查返回值来判断连接状态,进而进行相应的清理或处理。

接收新的连接

监听socket上有新的连接请求到达时,监听socket被标记为可读,可以调用accept函数来接收这个连接。

错误条件

socket上可能会发生错误,例如,连接被重置、超时、网络不可达等情况都可能导致socket状态不正常。当socket发生错误时,通常会将错误信息存储在一个内部状态中,使用getsockopt来读取和清除该错误。

socket可写条件

发送缓冲区可用

当发送缓冲区有可用字节,socket 会被标记为可写。

关闭连接

当对一个已关闭写通道的socket执行写操作时,通常会触发一个SIGPIPE信号。这种情况发生在尝试写入数据到一个已经关闭的连接时。

非阻塞模式

socket使用非阻塞connect连接成功或超时后,socket可写。

错误条件

socket上可能会发生错误,使用getsockopt来读取和清除该错误。

socket异常条件

接收带外数据

带外数据是一种特殊的传输方式,用于发送紧急数据。带外数据通常用于需要立即处理的重要信息,例如中断信号或控制信息。带外数据的接收被视为异常条件,需特别处理。

处理带外数据

server.cpp

#include <libgen.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <assert.h>
#include <errno.h>
#include <unistd.h>

int main(int argc, char* argv[]) {
    // 检查参数数量
    if (argc < 3) {
        printf("usage: %s ip_address, port number\n", basename(argv[0]));
        return -1;
    }
    const char* ip = argv[1];
    int port = atoi(argv[2]);

    int ret = 0;

    // 初始化地址结构
    struct sockaddr_in address = {0};
    address.sin_family = AF_INET;
    if (inet_pton(AF_INET, ip, &address.sin_addr) <= 0) {
        perror("Invalid address");
        return -1;
    }
    address.sin_port = htons(port);

    // 创建监听套接字
    int listenfd = socket(PF_INET, SOCK_STREAM, 0);
    assert(listenfd >= 0);

    // 绑定地址
    ret = bind(listenfd, (struct sockaddr*)&address, sizeof(struct sockaddr_in));
    assert(ret != -1);

    // 开始监听
    ret = listen(listenfd, 5);
    assert(ret != -1);
    
    struct sockaddr_in client_address = {0};
    socklen_t client_addrlength = sizeof(client_address);

    // 接受客户端连接
    int connfd = accept(listenfd, (struct sockaddr*)&client_address, &client_addrlength);
    if (connfd < 0) {
        printf("errno is: %d\n", errno);
        close(listenfd);
        return -1;  // 添加返回以结束程序
    }

    char buffer[1024];
    fd_set read_fds;
    fd_set exception_fds;
    FD_ZERO(&read_fds);
    FD_ZERO(&exception_fds);

    // 主循环,处理接收到的数据
    while (1) {
        memset(buffer, '\0', sizeof(buffer));

        FD_SET(connfd, &read_fds);
        FD_SET(connfd, &exception_fds);

        ret = select(connfd + 1, &read_fds, NULL, &exception_fds, NULL);
        if (ret < 0) {
            printf("selection failure\n");
            break;
        }
        
        // 处理正常数据
        if (FD_ISSET(connfd, &read_fds)) {
            ret = recv(connfd, buffer, sizeof(buffer) - 1, 0);
            if (ret <= 0) {
                break;  // 处理关闭连接
            }
            printf("get %d bytes of normal data: %s\n", ret, buffer);
        } 
        // 处理紧急数据
        else if (FD_ISSET(connfd, &exception_fds)) {
            ret = recv(connfd, buffer, sizeof(buffer) - 1, MSG_OOB);
            if (ret <= 0) {
                break;  // 处理关闭连接
            }
            printf("get %d bytes of oob data: %s\n", ret, buffer);
        }
    }
    close(connfd);
    close(listenfd);
    return 0;
}

client.cpp

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <arpa/inet.h>

#define PORT 2222
#define SERVER_IP "192.168.32.162"

int main() {
    int sockfd;
    struct sockaddr_in server_addr;

    // 创建套接字
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("Socket creation failed");
        return 1;
    }

    // 设置服务器地址
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);

    // 连接到服务器
    if (connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr)) < 0) {
        perror("Connection failed");
        close(sockfd);
        return 1;
    }

    // 发送普通数据
    const char *msg = "Hello, server!";
    send(sockfd, msg, strlen(msg), 0);

    // 发送带外数据
    const char *urgent_msg = "Urgent data!";
    send(sockfd, urgent_msg, strlen(urgent_msg), MSG_OOB);

    // 关闭套接字
    close(sockfd);
    return 0;
}

运行结果

先运行server.cpp,再运行client.cpp,结果如下。

推荐一下

0voice · GitHub

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

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

相关文章

flutter集成百度地图定位 ‘BMKLocationManager.h‘ file not found报错

一、写在前面 好久不见~最近接手了一个flutter的项目&#xff0c;需求是接入百度地图的定位插件。但是按照官网的文档来做&#xff0c;安卓没有问题&#xff0c;但是ios就惨了&#xff0c;各种编译报错。 flutter_bmflocation: ^3.6.0 集成报错 ‘BMKLocationManager.h’ fil…

Renesas R7FA8D1BH (Cortex®-M85)内部RTC的应用

目录 概述 1 软硬件 1.1 软硬件环境信息 1.2 开发板信息 1.3 调试器信息 2 FSP配置RTC 2.1 配置参数 2.2 RTC模块介绍 3 RTC相关函数 3.1 R_RTC_Open() 3.2 R_RTC_Close() 3.3 R_RTC_ClockSourceSet() 3.4 R_RTC_CalendarTimeSet() 3.5 R_RTC_CalendarTimeGet()…

HC-SR04超声波传感器详解(STM32)

目录 一、介绍 二、传感器原理 1.原理图 2.引脚描述 3.工作原理介绍 三、程序设计 main.c文件 ultrasonic.h文件 ultrasonic.c文件 四、实验效果 五、资料获取 项目分享 一、介绍 HC-SR04超声波传感器是通过发送和接收超声波&#xff0c;利用时间差和声音传播速度…

Python编码系列—Python团队开发工作流:高效协作的艺术

&#x1f31f;&#x1f31f; 欢迎来到我的技术小筑&#xff0c;一个专为技术探索者打造的交流空间。在这里&#xff0c;我们不仅分享代码的智慧&#xff0c;还探讨技术的深度与广度。无论您是资深开发者还是技术新手&#xff0c;这里都有一片属于您的天空。让我们在知识的海洋中…

煤炭检测系统源码分享

煤炭检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Vision …

A股上市公司企业创新能力、质量、效率-原始数据+dofile+结果(2006-2023年)

上市公司的创新能力体现在其不断研发新技术、新产品和服务的能力上&#xff0c;这是企业保持竞争优势的关键&#xff1b;质量则是指公司所提供的产品或服务达到高标准的程度&#xff0c;高质量是赢得客户信任和市场份额的基础&#xff1b;效率则涵盖了生产运营中的资源利用程度…

天线工程师进阶指南:只会割铜皮式调天线,就Out了!跨学科天线设计介绍

❝本次推文简单介绍下跨学科天线设计。 什么是天线&#xff1f; 天线是一种变换器&#xff0c;它把传输线上传播的导行波&#xff0c;变换成在无界媒介&#xff08;通常是自由空间&#xff09;中传播的电磁波&#xff0c;或者进行相反的变换。 发射天线可以将来自发射机的高频…

资源创建方式

kubernetes支持两种创建资源的方式&#xff1a; 用kubectl命令直接创建&#xff0c;比如&#xff1a;kubectl run nginx-deployment --imagenginx1.7.9 --replicas2&#xff0c;在命令行中通过参数指定资源的属性 通过配置文件和kubectl apply创建&#xff0c;创建nginx.yml文…

9月18日

思维导图 配置桥接网络的过程 配置桥接网络 确保虚拟机提供了桥接模式菜单栏>编辑>虚拟机网络编辑器确保虚拟机可以设置桥接网络&#xff08;如无法通过桥接连接网络&#xff0c;则可以还原设置后重新尝试&#xff0c;如果还不行则找到VMware的软件安装包&#xff0c;双…

Pc端关于不同PDF阅读器的实际体验

因为马上研究生开学了&#xff0c;平时也会阅读很多pdf&#xff0c;实际上我们电脑上也自带一个pdf阅读的软件&#xff1a;也就是我们的edge&#xff0c;但是还是可能有些不够我们使用。下面是一些容易获取到的软件资源。 下面的评价仅是个人观点&#xff0c;请理性看待。 一…

【Pycharm使用技巧记录手册】批量检索与替换功能——辅助Yolo训练标签label配置文件构建

在yolo训练前的准备工作中&#xff0c;需要编写yaml配置文件中的信息。对于多分类问题&#xff0c;需要将其类别与索引一一对应。实践中&#xff0c;类别与索引的关系可能写在字典数据格式内&#xff0c;如何将其转换为配置文件内的信息&#xff0c;这是一个看起来简单但如果纯…

亲测有效,长期有效的RTSP流地址公网RTSP地址,各种类型的视频源

我们经常需要做一些实时视频流的测试&#xff0c;但是手边又没有办法及时弄到一个摄像机&#xff0c;我们经常会去搜索一下“公网RTSP地址”&#xff0c;但是大部分现在都失效了&#xff0c;有什么办法能够让我们快速构建一个RTSP流&#xff0c;点几下就能直接用&#xff1f; …

yolov5/8/9/10模型在车辆检测中的应用【代码+数据集+python环境+GUI系统】

yolov5/8/9/10模型在车辆检测中的应用【代码数据集python环境GUI系统】 1.背景意义 随着城市化进程的加速和机动车数量的激增&#xff0c;交通拥堵、交通事故等问题日益严重。传统的交通管理手段已经难以满足日益增长的交通需求。基于计算机视觉的车辆检测技术通过实时捕捉道路…

C/C++语言基础--C++面向对象、类、对象概念讲解

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 今天更新的比较晚了&#xff0c;主要一直用是谷歌Colab训练模型&#xff0c;访问国内csdn反而不好使了&#xff0c;请大家见谅&#xff1b;C是面向对象的语言&#xff0c;本文将介绍什么是面向对象、什么是类…

jmeter+ant+git+jenkins

基于工具的接口自动化&#xff08;jmeterantgitjenkins&#xff09; 1.1 简介 Jmeter、Ant、Git和Jenkins是一套结合了测试、代码管理和持续集成/持续部署&#xff08;CI/CD&#xff09;的工具链&#xff0c;可以帮助开发者进行高效的接口开发和测试。这四个工具可以相互配合…

828华为云征文|华为云Flexus云服务器X实例之openEuler系统下部署SQLite数据库浏览器sqlite-web

828华为云征文&#xff5c;华为云Flexus云服务器X实例之openEuler系统下部署SQLite数据库浏览器sqlite-web 前言一、Flexus云服务器X实例介绍1.1 Flexus云服务器X实例简介1.2 Flexus云服务器X实例特点1.3 Flexus云服务器X实例使用场景 二、sqlite-web介绍2.1 sqlite-web简介2.2…

C语言 | Leetcode C语言题解之第406题根据身高重建队列

题目&#xff1a; 题解&#xff1a; int cmp(const void* _a, const void* _b) {int *a *(int**)_a, *b *(int**)_b;return a[0] b[0] ? a[1] - b[1] : b[0] - a[0]; }int** reconstructQueue(int** people, int peopleSize, int* peopleColSize, int* returnSize, int** …

TypeScript入门 (二)控制语句

引言 大家好&#xff0c;我是GISer Liu&#x1f601;&#xff0c;一名热爱AI技术的GIS开发者。本系列文章是我跟随DataWhale 2024年9月学习赛的TypeScript学习总结文档。本文主要讲解TypeScript中控制语句的部分&#xff1b;希望通过我的知识点总结&#xff0c;能够帮助你更好地…

OpenCV基础入门30讲(Python)——第一讲 环境配置

学习基础要求&#xff1a; 1、会Python。 版本要求&#xff1a; 1、电脑系统&#xff1a;Windows10&#xff08;理论上Ubuntu或者Windows10和Windows11都可以&#xff09; 2、Python版本&#xff1a;Python 3.8&#xff08;理论上Python 3.6-3.9都可以&#xff09; 3、OpenCV版…

微博计算架构实战

课前回顾-性能估算方法 用户量预估 用户行为建模和性能估算 高性能计算架构设计 发微博 发微博是写操作&#xff0c;可以用与写缓冲(Buffer)么&#xff1f;最好是不要加&#xff0c;写缓冲会使得写入速度变慢&#xff0c;比如发送了微博10分钟后&#xff0c;别人还看不到&…