27-LINUX--I/O复用-poll

一.poll概述

poll是一个多路复用的I/O模型,一个进程监视多个文件描述符,当文件描述符就绪时,poll返回可读并做相应处理。

1.poll的模型

复制代码
#include <poll.h>

struct pollfd
{
    int fd;         //文件描述符
    short events;   //事件类型 
    short revents;  //实际发送事件
}

int poll(struct pollfd *fds, nfds_t nfds, int timeout);

 /*
 poll 系统调用成功返回就绪文件描述符的总数,超时返回 0,失败返回-1

 nfds 参数指定被监听事件集合 fds 的大小。
 timeout 参数指定 poll 的超时值,单位是毫秒,timeout 为-1 时,poll 调用将永久
阻塞,直到某个事件发生,timeout 为 0 时,poll 调用将立即返回。

2.事件类型

二.测试代码

SER.C

复制代码
#include<stdio.h>      // 标准输入输出库
#include<stdlib.h>     // 标准库,提供动态内存分配等
#include<string.h>    // 字符串操作库
#include<unistd.h>    // UNIX标准函数库,提供close函数等
#include<sys/socket.h>// 套接字库
#include<netinet/in.h> // 网络头文件,提供IPv4地址格式
#include<arpa/inet.h> // 网络地址转换库
#include<poll.h>      // poll系统调用

#define MAXFD 10       // 定义最大的文件描述符数量

// 初始化socket函数
int socket_init() {
    int sockfd = socket(AF_INET, SOCK_STREAM, 0); // 创建socket
    if (sockfd == -1) {
        return -1; // 如果创建失败,返回-1
    }

    struct sockaddr_in saddr; // 服务器地址结构
    memset(&saddr, 0, sizeof(saddr)); // 清零
    saddr.sin_family = AF_INET; // 地址族
    saddr.sin_port = htons(6000); // 端口号
    saddr.sin_addr.s_addr = inet_addr("127.0.0.1"); // IP地址

    int res = bind(sockfd, (struct sockaddr*)&saddr, sizeof(saddr)); // 绑定地址
    if (res == -1) {
        printf("bind err\n");
        return -1; // 绑定失败,返回-1
    }
    if (listen(sockfd, 5) == -1) { // 开始监听,设置队列长度为5
        return -1; // 监听失败,返回-1
    }
    return sockfd; // 返回socket文件描述符
}

// 初始化pollfd数组
void fds_init(struct pollfd fds[]) {
    for (int i = 0; i < MAXFD; i++) {
        fds[i].fd = -1; // 文件描述符设置为-1,表示未使用
        fds[i].events = 0; // 事件掩码设置为0
        fds[i].revents = 0; // 事件返回掩码设置为0
    }
}

// 将新的文件描述符添加到pollfd数组
void fds_add(struct pollfd fds[], int fd) {
    for (int i = 0; i < MAXFD; i++) {
        if (fds[i].fd == -1) {
            fds[i].fd = fd; // 设置文件描述符
            fds[i].events = POLLIN; // 设置感兴趣的事件为POLLIN
            fds[i].revents = 0; // 重置事件返回掩码
            break; // 退出循环
        }
    }
}

// 从未使用的pollfd数组中删除文件描述符
void fds_del(struct pollfd fds[], int fd) {
    for (int i = 0; i < MAXFD; i++) {
        if (fds[i].fd == fd) {
            fds[i].fd = -1; // 将文件描述符重置为-1
            fds[i].events = 0; // 重置事件掩码
            fds[i].revents = 0; // 重置事件返回掩码
            break; // 退出循环
        }
    }
}

// 接受客户端连接请求并添加到pollfd数组
void accept_cli(int sockfd, struct pollfd fds[]) {
    int c = accept(sockfd, NULL, NULL); // 接受连接
    if (c < 0) {
        return; // 如果返回-1,表示出错
    }
    printf("accept c = %d\n", c);
    fds_add(fds, c); // 添加到pollfd数组
}

// 接收客户端数据
void recv_data(int c, struct pollfd fds[]) {
    char buff[128] = {0}; // 创建接收缓冲区
    int n = recv(c, buff, 127, 0); // 接收数据
    if (n <= 0) {
        close(c); // 如果接收失败或客户端关闭连接,则关闭socket
        printf("cli close = %d\n", c);
        fds_del(fds, c); // 从pollfd数组中删除该文件描述符
        return;
    }
    printf("buff(%d)=%s\n", c, buff); // 打印接收到的数据
    send(c, "ok", 2, 0); // 发送确认消息给客户端
}

// 主函数
int main() {
    int sockfd = socket_init(); // 初始化socket
    if (sockfd == -1) {
        exit(1); // 如果初始化失败,退出程序
    }

    struct pollfd fds[MAXFD]; // 创建pollfd数组
    fds_init(fds); // 初始化数组
    fds_add(fds, sockfd); // 将监听的socket添加到数组

    while (1) { // 无限循环,等待事件
        int n = poll(fds, MAXFD, 5000); // 调用poll等待最多5000毫秒
        if (n == -1) { // 如果poll调用失败
            printf("poll err\n");
        } else if (n == 0) { // 如果超时
            printf("time out\n");
        } else { // 如果有事件发生
            for (int i = 0; i < MAXFD; i++) { // 遍历pollfd数组
                if (fds[i].fd == -1) { // 如果文件描述符未使用,跳过
                    continue;
                }
                if (fds[i].revents & POLLIN) { // 如果有可读事件发生
                    if (fds[i].fd == sockfd) { // 如果是监听的socket
                        accept_cli(sockfd, fds); // 接受新的客户端连接
                    } else { // 如果是已连接的客户端
                        recv_data(fds[i].fd, fds); // 接收数据
                    }
                }
            }
        }
    }
}
相关推荐
林姜泽樾1 小时前
Linux入门第十二章,创建用户、用户组、主组附加组等相关知识详解
linux·运维·服务器·centos
xiaokangzhe2 小时前
Linux系统安全
linux·运维·系统安全
feng一样的男子2 小时前
NFS 扩展属性 (xattr) 提示操作不支持解决方案
linux·go
南棱笑笑生2 小时前
20260310在瑞芯微原厂RK3576的Android14查看系统休眠时间
服务器·网络·数据库·rockchip
xiaokangzhe2 小时前
Nginx核心功能
运维·nginx
松果1772 小时前
以本地时钟为源的时间服务器
运维·chrony·时间服务器
XDHCOM2 小时前
ORA-32152报错咋整啊,数据库操作遇到null number问题远程帮忙修复
服务器·数据库·oracle
Highcharts.js3 小时前
Highcharts React v4.2.1 正式发布:更自然的React开发体验,更清晰的数据处理
linux·运维·javascript·ubuntu·react.js·数据可视化·highcharts
ayaya_mana3 小时前
快速安装Nginx-UI:让Nginx管理可视化的高效方案
运维·nginx·ui
c++之路3 小时前
Linux网络协议与编程基础:TCP/IP协议族全解析
linux·网络协议·tcp/ip