Linux应用 select编程

1、概念

1.1 多路复用

在Linux中,多路复用是一种机制,用于同时监视多个文件描述符的状态,以便在其中任何一个文件描述符准备好进行读写操作时立即通知进程。常见的多路复用机制包括 select、poll 和 epoll。

1.2 select

select 是一种用于实现异步通信的系统调用。它允许进程监视多个文件描述符(包括套接字、管道等)的状态,当其中任何一个文件描述符准备好进行读或写操作时,select 就会通知进程。select 的原理是通过在内核中维护一个数据结构来管理文件描述符的状态,当进程调用 select 时,内核会检查所监视的文件描述符的状态,并根据情况进行相应的处理。

1.3 应用场景

  • 并发服务器:在服务器端可以同时处理多个客户端连接请求,使用 select 可以监视多个客户端的套接字,当有客户端数据到达时立即处理。
  • I/O多路复用:在客户端,可以同时处理多个文件描述符的读写操作,节省了不必要的阻塞等待时间。
  • 实时性要求高:当需要实时响应多个文件描述符的状态变化时,select 可以及时通知进程。

1.4 特点

  • 监视多个文件描述符select 可以同时监视多个文件描述符,包括套接字、管道、标准输入等。
  • 非阻塞等待select 可以让进程在等待多个文件描述符准备就绪的过程中继续执行其他任务,不会阻塞在一个文件描述符上。
  • 跨平台支持select 是跨平台的系统调用,在不同操作系统上都能使用。
  • 简单易用:相比其他多路复用机制如 poll 和 epollselect 使用简单,适用于一些简单的并发场景。

2、编程接口

2.1 select

select 函数用于监视一组文件描述符,当其中任何一个文件描述符准备好进行 I/O 操作时,select 函数就会返回,告知应用程序哪些文件描述符可以进行读取、写入或发生异常。

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

    • nfds:监视的文件描述符中最大的文件描述符值加一。
    • readfds:指向一个 fd_set 集合,包含了需要监视读取操作的文件描述符。
    • writefds:指向一个 fd_set 集合,包含了需要监视写入操作的文件描述符。
    • exceptfds:指向一个 fd_set 集合,包含了需要监视异常情况的文件描述符。
    • timeout:指定 select 函数的超时时间,可以是 NULL(阻塞直到有文件描述符准备好)、struct timeval 结构体(设置超时时间)或者 {0, 0}(立即返回)。
  • 返回值

    • 大于 0:返回准备就绪的文件描述符的数量。
    • 等于 0:超时时间到达,没有文件描述符准备好。
    • 小于 0:出错,返回 -1。

2.2 fd_set

在 Linux C 语言编程中,fd_set 是一个用于表示文件描述符集合的数据结构,通常用于多路复用函数(如 selectpollepoll)中。fd_set 提供了一系列操作函数来对文件描述符集合进行操作。

2.2.1 FD_ZERO

将一个 fd_set 集合清空,将所有文件描述符从集合中移除。

void FD_ZERO(fd_set *set);
  • 入参

    • set:要移除文件描述符的集合。
2.2.2 FD_SET

将指定的文件描述符添加到 fd_set 集合中。

void FD_SET(int fd, fd_set *set);
  • 入参

    • fd 表示要添加的文件描述符
    • set 表示要添加到的 fd_set 集合
2.2.3 FD_CLR

从 fd_set 集合中移除指定的文件描述符。

void FD_CLR(int fd, fd_set *set);
  • 入参

    • fd 表示要移除的文件描述符
    • set 表示要从中移除的 fd_set 集合
2.2.4 FD_ISSET

检查指定的文件描述符是否在 fd_set 集合中。

int FD_ISSET(int fd, fd_set *set);
  • 入参

    • fd 表示要检查的文件描述符
    • set 表示要检查的 fd_set 集合
  • 返回值

    • 如果文件描述符在集合中,返回非零值;否则返回 0。

3、编程测试

3.1 服务器编程

编写服务器测试程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8888
#define MAX_CLIENTS 5
#define BUFFER_SIZE 1024

int main() 
{
    int server_fd, new_socket, max_clients = MAX_CLIENTS;
    struct sockaddr_in address;
    int addrlen = sizeof(address);
    fd_set readfds;
    int client_sockets[MAX_CLIENTS] = {0};
    char buffer[BUFFER_SIZE] = {0};

    // Create a TCP socket
    if ((server_fd = socket(AF_INET, SOCK_STREAM, 0)) == 0) 
    {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    address.sin_family = AF_INET;
    address.sin_addr.s_addr = INADDR_ANY;
    address.sin_port = htons(PORT);

    // Bind the socket to localhost:PORT
    if (bind(server_fd, (struct sockaddr *)&address, sizeof(address)) < 0) 
    {
        perror("Bind failed");
        exit(EXIT_FAILURE);
    }

    // Listen for incoming connections
    if (listen(server_fd, 3) < 0) 
    {
        perror("Listen failed");
        exit(EXIT_FAILURE);
    }

    printf("Server listening on port %d\n", PORT);

    while (1) 
    {
        FD_ZERO(&readfds);
        FD_SET(server_fd, &readfds);

        int max_sd = server_fd;

        for (int i = 0; i < max_clients; i++) 
        {
            int sd = client_sockets[i];

            if (sd > 0) 
            {
                FD_SET(sd, &readfds);
            }

            if (sd > max_sd) 
            {
                max_sd = sd;
            }
        }

        // Select the file descriptors
        int activity = select(max_sd + 1, &readfds, NULL, NULL, NULL);

        if (FD_ISSET(server_fd, &readfds)) 
        {
            if ((new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen)) < 0) 
            {
                perror("Accept failed");
                exit(EXIT_FAILURE);
            }

            printf("New connection, socket fd is %d, IP is : %s, port : %d\n", new_socket, inet_ntoa(address.sin_addr), ntohs(address.sin_port));

            for (int i = 0; i < max_clients; i++) 
            {
                if (client_sockets[i] == 0) 
                {
                    client_sockets[i] = new_socket;
                    break;
                }
            }
        }

        for (int i = 0; i < max_clients; i++) 
        {
            int sd = client_sockets[i];

            if (FD_ISSET(sd, &readfds)) 
            {
                int valread = read(sd, buffer, BUFFER_SIZE);
                if (valread == 0) 
                {
                    // Client disconnected
                    close(sd);
                    client_sockets[i] = 0;
                } 
                else 
                {
                    // Process data from client
                    printf("Received from client: %s\n", buffer);

                    // Add prefix to the received data
                    char reply[BUFFER_SIZE + 15];
                    sprintf(reply, "Server Recv Ok: %s", buffer);

                    // Send the modified data back to client
                    send(sd, reply, strlen(reply), 0);
                }
            }
        }
    }

    return 0;
}

3.2 客户端编程

编写客户端测试程序如下:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#define PORT 8888
#define SERVER_IP "127.0.0.1"
#define BUFFER_SIZE 1024

int main() 
{
    int client_fd;
    struct sockaddr_in server_address;
    char buffer[BUFFER_SIZE] = {0};

    // Create a TCP socket
    if ((client_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) 
    {
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    server_address.sin_family = AF_INET;
    server_address.sin_port = htons(PORT);

    // Convert IPv4 and IPv6 addresses from text to binary form
    if (inet_pton(AF_INET, SERVER_IP, &server_address.sin_addr) <= 0) 
    {
        perror("Invalid address/ Address not supported");
        exit(EXIT_FAILURE);
    }

    // Connect to the server
    if (connect(client_fd, (struct sockaddr *)&server_address, sizeof(server_address)) < 0) 
    {
        perror("Connection failed");
        exit(EXIT_FAILURE);
    }

    printf("Connected to server\n");

    while (1) 
    {
        printf("Enter message to send (or 'exit' to quit): ");
        fgets(buffer, BUFFER_SIZE, stdin);

        if (strcmp(buffer, "exit\n") == 0) 
        {
            break;
        }

        // Send data to server
        send(client_fd, buffer, strlen(buffer), 0);

        // Receive response from server
        int valread = read(client_fd, buffer, BUFFER_SIZE);
        printf("Server response: %s\n", buffer);
    }

    close(client_fd);

    return 0;
}
3.3 测试

发起服务器程序,然后开启多个终端分别发起客户端程序,客户端程序接收控制台输入数据后发送给服务器,服务器添加一个头部后将数据发送回客户端,测试结果如下:

4、总结

本文介绍了select的相关概念,详细阐述了编程常用接口,最后在linux下进行编程测试。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【aws】在DBeaver上用终端节点连接Redshift

碎碎念 最近想要尝试redshift的一个叫做重新定位的功能&#xff0c;重新定位触发之后会停止当前的集群&#xff0c;转而在同一个区域的另一个可用区中启动一个一样的集群&#xff0c;这个过程视情况会花上10到60分钟不等。 但是目前项目中连接到redshift用的是私有ip&#xf…

C# Window form 自定义控件的结构和设计(三)

C# Window form 自定义控件的结构和设计(三) 一、前面介绍了如何来创建第一个自定义的控件&#xff0c;以及一个测试程序。下面我们来看下如何在自定义控件中添加属性。 C#和其他.NET语言支持属性作为语言的第一类成员。把属性作为语言的基础属性有两点主要的有点&#xff1a…

foreach无法修改数组值解决方案

效果展示&#xff1a; 解决办法&#xff1a; this.sportList.forEach((item,index) >{let that this;if(item.idinfo.id) {that.sportList[index].sportTime e.detail.value} }) 这里小编解释下&#xff0c;将this赋值给that通常是为了在回调函数或者异步代码中保持对Vu…

Android安卓开发 - 开发基础(二)

App的工程结构 本节介绍App工程的基本结构及其常用配置&#xff0c;首先描述项目和模块的区别&#xff0c;以及工程内部各目录与配置 文件的用途说明&#xff1b;其次阐述两种级别的编译配置文件build.gradle…

吴恩达2022机器学习专项课程(一) 第二周课程实验:特征工程和多项式回归(Lab_04)

目标 探索特征工程和多项式回归&#xff0c;使用线性回归来拟合非常复杂甚至非线性的函数。 1.为什么线性回归能拟合非线性函数&#xff1f; fxw*xb&#xff0c;属于线性回归的扩展&#xff0c;这个公式在数学中不属于线性&#xff0c;因为有x&#xff0c;而在机器学习中属于…

接口的三个常见使用案例

下面的三个案例&#xff0c;都是需要实现接口&#xff0c;才能进行的操作。 目录 1.比较对象大小 2.给对象排序 3.深浅拷贝 1.比较对象大小 1.1引入 &#xff08;1&#xff09;普通类型比较 &#xff08;2&#xff09;引入类型比较 发现报错&#xff0c;因为在Java中&…

如何理解Vue 3组件的component关键字

&#x1f90d; 前端开发工程师、技术日更博主、已过CET6 &#x1f368; 阿珊和她的猫_CSDN博客专家、23年度博客之星前端领域TOP1 &#x1f560; 牛客高级专题作者、打造专栏《前端面试必备》 、《2024面试高频手撕题》 &#x1f35a; 蓝桥云课签约作者、上架课程《Vue.js 和 E…

新质生产力与智能制造:推动制造业转型升级的双引擎

引言 随着科技的不断进步和全球制造业的快速发展&#xff0c;新质生产力与智能制造成为推动制造业转型升级的关键驱动力。新质生产力强调的是以科技创新和制度创新为核心&#xff0c;通过提高生产效率和经济效益来推动经济发展。而智能制造则是利用现代信息技术&#xff0c;实现…

window2012等显示桌面上面的【我的电脑】图标

服务器版本&#xff0c;登录进去&#xff0c;就留个垃圾桶在桌面上&#xff0c;一点也没有用处。 如何显示经典的“我的电脑”呢。 网络介绍了几种方法都木用。这种还点用&#xff1a; win图标点右键》运行&#xff1a; rundll32.exe shell32.dll,Control_RunDLL desk.cpl,…

【系统分析师】计算机网络

文章目录 1、TCP/IP协议族1.1 DHCP协议1.2 DNS协议1.3网络故障诊断 2、网路规划与设计2.1逻辑网络设计2.2物理网络设计2.3 分层设计 3、网络接入3.1 接入方式3.2 IPv6地址 4、综合布线技术5、物联网5.1物联网概念与分层5.2 物联网关键技术 6、云计算7、网络存储技术&#xff08…

使用odbc链接dm8数据库

一、环境说明 windows11 VMware Workstation 17 Pro ubuntu22.04 docker $ lsb_release -a No LSB modules are available. Distributor ID: Ubuntu Description: Ubuntu 22.04.3 LTS Release: 22.04 Codename: jammy因docker版本的dm8中&#xff0c;没有…

【每日刷题】代码随想录-动规32

1. 代码随想录-动规32.LC121买卖股票的最佳时机 题目链接 不用动规。双指针法。快指针遍历&#xff0c;慢指针指向最小的。 max维护最大差值。 min第一天价格&#xff0c;如果碰到有比min低的&#xff0c;则更新min。 代码 public int maxProfit(int[] prices) {int min pr…

国内开通gpt会员方法

ChatGPT镜像 今天在知乎看到一个问题&#xff1a;“平民不参与内测的话没有账号还有机会使用ChatGPT吗&#xff1f;” 从去年GPT大火到现在&#xff0c;关于GPT的消息铺天盖地&#xff0c;真要有心想要去用&#xff0c;途径很多&#xff0c;别的不说&#xff0c;国内GPT的镜像…

【WinForm】如何在自己的程序窗口中显示并调用外部桌面程序

当你爱上一个程序的功能&#xff0c;并且希望扩展它以满足自己的需求时&#xff0c;你可能会觉得困惑。毕竟&#xff0c;你已经为此付出了很多努力&#xff0c;并希望能够有效地整合这些功能。那么&#xff0c;是否可以将这些功能嵌套到自己的程序中呢&#xff1f; 首先&#…

三年了,期待下一个三年

第一个三年 时间好快&#xff0c;距离我发布我第一篇文章都已经三个年头了。 转眼也从大一新生变成了大四打工人。 在平台上发布博客&#xff0c;分享自己的项目、学习思路、解决的bug都带给我很多收获。 平台上的粉丝&#xff0c;阅读量等&#xff0c;也让我的简历更加出彩。…

数据结构初阶:二叉树(二)

二叉树链式结构的实现 前置说明 在学习二叉树的基本操作前&#xff0c;需先要创建一棵二叉树&#xff0c;然后才能学习其相关的基本操作。由于现在对二叉树结构掌握还不够深入&#xff0c;为了降低学习成本&#xff0c;此处手动快速创建一棵简单的二叉树&#xff0c;快速进入二…

【MATLAB应用】去噪算法

01.引言 图像的产生是电子和光学相互作用的结果&#xff0c;而图像中的噪声则是由于成像过程中的颗粒性质而客观存在的。不同类型的噪声从不同的视角产生&#xff0c;各自具有特点。因此&#xff0c;有效地去除图像中的噪声以获得更高质量的图像具有实际意义。目前存在多种图像…

IPD集成产品开发

时间&#xff1a;2024年04月14日 作者&#xff1a;小蒋聊技术 邮箱&#xff1a;wei_wei10163.com 微信&#xff1a;wei_wei10 解密IPD集成产品开发_小蒋聊技术_免费在线阅读收听下载 - 喜马拉雅欢迎收听小蒋聊技术的类最新章节声音“解密IPD集成产品开发”。大家好&#xff…

MAT工具详解

简介 Java自带的JVisualVm可以用来分析Java堆内存&#xff0c;可以用来排查内存泄漏和内存浪费的问题&#xff0c;但是功能不是特别强大&#xff0c; MAT&#xff08;Memory Aanlysis Tool&#xff09;是一款更优的工具。 MAT功能 功能组 全局信息 直方图 按照类的数量倒序…

AI大模型日报#0413:谷歌引入“无限注意力”、Picsart AI 开源120秒超长AI视频模型

导读&#xff1a; 欢迎阅读《AI大模型日报》&#xff0c;内容基于Python爬虫和LLM自动生成。目前采用“文心一言”生成了每条资讯的摘要。 标题: 速递&#xff5c;木头姐 ARK 宣布已投资 OpenAI&#xff01;还将 Anthropic 及 Figure1 等 AI 独角兽一网打尽摘要: ARK已通过其风…