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); // 接收数据
                    }
                }
            }
        }
    }
}
相关推荐
oi7717 分钟前
使用itextpdf进行pdf模版填充中文文本时部分字不显示问题
java·服务器
AttackingLin25 分钟前
2024强网杯--babyheap house of apple2解法
linux·开发语言·python
吃肉不能购2 小时前
Label-studio-ml-backend 和YOLOV8 YOLO11自动化标注,目标检测,实例分割,图像分类,关键点估计,视频跟踪
运维·yolo·自动化
学Linux的语莫2 小时前
Ansible使用简介和基础使用
linux·运维·服务器·nginx·云计算·ansible
qq_312920112 小时前
docker 部署 kvm 图形化管理工具 WebVirtMgr
运维·docker·容器
踏雪Vernon2 小时前
[OpenHarmony5.0][Docker][环境]OpenHarmony5.0 Docker编译环境镜像下载以及使用方式
linux·docker·容器·harmonyos
Onlooker1292 小时前
云服务器部署WebSocket项目
服务器
学Linux的语莫2 小时前
搭建服务器VPN,Linux客户端连接WireGuard,Windows客户端连接WireGuard
linux·运维·服务器