IO多路复用-poll(附通信代码)

C语言 IO多路复用——poll函数 友链 gcc 1.c -o 1 -lpthread ctrl+f搜索服务端代码和客户端代码获取代码 该方法也可以实现并发服务器 IO多路复用IO多路转接 简介 IO多路复用中的IO并不是指单纯的标准输入和输出 这里的IO指的是程序和内存之间的IO 从程序将数据写入内存就叫做输出 从内存中将数据读入程序中就叫做输入 socket通信过程中也牵涉到大量的IO操作 在unix中,socket不过就是一个文件描述符,对应内核中的一块缓冲区 缓冲区分为读缓冲区和写缓冲区 IO多路复用具体指的就是对读写缓冲区的操作 阅读详情

IO多路复用-poll

1. poll函数

select函数的比较

  • 内核对应文件描述符的检测也是以线性的方式进行轮询,根据描述符的状态进行处理
  • poll和select检测的文件描述符集合会在检测过程中频繁的进行用户区和内核区的拷贝,它的开销随着文件描述符数量的增加而线性增大,从而效率也会越来越低。
  • select检测的文件描述符个数上限是1024,poll没有最大文件描述符数量的限制
  • select可以跨平台使用,poll只能在Linux平台使用

poll函数的原型如下:

#include <poll.h>
// 每个委托poll检测的fd都对应这样一个结构体
struct pollfd {
    int   fd;         /* 委托内核检测的文件描述符 */
    short events;     /* 委托内核检测文件描述符的什么事件 */
    short revents;    /* 文件描述符实际发生的事件 -> 传出 */
};

struct pollfd myfd[100];
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

先看**struct pollfd结构体**:

  • fd:委托内核检测的文件描述符
  • events:委托内核检测的fd事件(输入、输出、错误),每一个事件有多个取值
  • revents:这是一个传出参数,数据由内核写入,存储内核检测之后的结果

在这里插入图片描述

再看poll函数的参数

  • fds: 这是一个struct pollfd类型的数组, 里边存储了待检测的文件描述符的信息

  • nfds: 这是第一个参数数组中最后一个有效元素的下标 + 1(也可以指定参数1数组的元素总个数)

  • timeout: 指定poll函数的阻塞时长

    • -1:一直阻塞,直到检测的集合中有就绪的文件描述符(有事件产生)解除阻塞

    • 0:不阻塞,不管检测集合中有没有已就绪的文件描述符,函数马上返回

    • 大于0:阻塞指定的毫秒(ms)数之后,解除阻塞

函数返回值:

  • 失败: 返回-1
  • 成功:返回一个大于0的整数,表示检测的集合中已就绪的文件描述符的总个数

2. 通信代码

服务器端

//
// Created by 47468 on 2024/1/25.
// server.cpp
#include "arpa/inet.h"
#include "poll.h"
#include "unistd.h"
#include <cstdio>
#include "cstdlib"
#include "iostream"
using namespace std;

int main(){
    // 1.创建套接字
    int lfd = socket(AF_INET, SOCK_STREAM, 0);
    if(lfd == -1){
        perror("socket");
        exit(0);
    }

    // 2. 绑定 ip, port
    struct sockaddr_in saddr{};
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    saddr.sin_addr.s_addr = INADDR_ANY;
    int res = bind(lfd, (struct sockaddr*)&saddr, sizeof(saddr));
    if(res == -1){
        perror("bind");
        exit(0);
    }

    // 3. 监听
    res = listen(lfd, 128);
    if(res == -1){
        perror("listen");
        exit(0);
    }

    // 4. 等待连接 -> 循环
    // 检测 -> 读缓冲区, 委托内核去处理
    // 数据初始化, 创建自定义的文件描述符集
    pollfd fds[1024];
    // 初始化
    for (int i = 0; i < 1024; ++i) {
        fds[i].fd = -1;
        fds[i].events = POLLIN; // 检测读事件
    }
    // 把监听描述符放到0索引
    fds[0].fd = lfd;
    int maxfd = 0;

    while (true){
        // 委托内核检测
        res = poll(fds, maxfd + 1, -1); // 一直阻塞, 直到检测到
        if(res == -1){
            perror("poll");
            exit(0);
        }

        // 检测的读缓冲区有变化
        // 有新连接, 检测的是监听描述符, 下表为0的那个
        if(fds[0].revents & POLLIN){
            // 接收连接请求
            sockaddr_in cliaddr{};
            int len = sizeof(cliaddr);
            int cfd = accept(fds[0].fd, (sockaddr*)&cliaddr, (socklen_t*)(&len));
            // 输出客户端信息
            char ip[32];
            cout << "有客户端连接, 客户端ip: "
                 << inet_ntop(AF_INET, &cliaddr.sin_addr.s_addr, ip, sizeof(ip))
                 << ", 端口: "
                 << ntohs(cliaddr.sin_port)
                 << endl;
            int i;
            for(i = 1; i < 1024; ++i){
                if(fds[i].fd == -1){ // 这个下标可用
                    fds[i].fd = cfd;
                    break;
                }
            }
            maxfd = max(maxfd, i);
        }

        // 通信, 有客户端发送数据过来
        for (int i = 1; i <= maxfd; ++i) {
            if(fds[i].revents & POLLIN){ // 说明读缓冲区有数据
                char buf[100];
                ssize_t len = read(fds[i].fd, buf, sizeof(buf));
                if(len == -1){
                    perror("read");
                    exit(0);
                }
                else if(len == 0){
                    cout << "客户端断开了连接" << endl;
                    close(fds[i].fd);
                    fds[i].fd = -1;
                }
                else{
                    buf[len] = '\0';
                    cout << "客户端说: " << buf << endl;
                    for(int j = 0; j < len; ++j){
                        buf[j] = toupper(buf[j]);
                    }
                    write(fds[i].fd, buf, len);
                }
                
            }
        }

    }
    close(lfd);
    return 0;
}

客户端

//
// Created by 47468 on 2024/1/25.
//
#include "arpa/inet.h"
#include "poll.h"
#include "unistd.h"
#include <cstdio>
#include <cstring>
#include "cstdlib"
#include "iostream"
using namespace std;

int main(){
    // 1. 创建用于通信的套接字
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if(fd == -1){
        perror("socket");
        exit(0);
    }

    // 2. 连接服务器
    sockaddr_in saddr{};
    saddr.sin_family = AF_INET;
    saddr.sin_port = htons(9999);
    inet_pton(AF_INET, "192.168.110.129", &saddr.sin_addr.s_addr);
    int res = connect(fd, (sockaddr *) &saddr, sizeof(saddr));
    if(res == -1){
        perror("connet");
        exit(0);
    }
    
    // 通信
    while(true){
        // 读数据
        char readBuf[1024];
        // 写数据
        cout << "请输入要发送的字符串: " << endl;
        cin.getline(readBuf, sizeof(readBuf));
        // 发送数据到客户端
        write(fd, readBuf, strlen(readBuf));
        // 接收服务器发送的数据
        read(fd, readBuf, sizeof(readBuf));
        cout << readBuf << endl;
    }
    close(fd);
    return 0;
}

测试:

服务器:

在这里插入图片描述

两个客户端:

在这里插入图片描述

在这里插入图片描述

IO多路复用之select、poll、epoll之间的区别总结 select、poll、epoll都是IO多路复用的机制。IO多路复用就是通过一种机制,让一个进程/线程可以监视多个描述符,一旦某个描述符就绪(一般是读写就绪),能够通知应用程序进行相应的读写操作。I/O复用虽然能同时监听多个文件描述符,当其本质上还是同步IO模型,因为需要在读写事件就绪后程序自己负责进行读写事件的处理,而这个读写过程是阻塞的。如果要实现并发,只能使用多进程/多线程等编程手段了。 ... 阅读详情

相关推荐

IO多路复用原理深度总结【万字总结】

阻塞和非阻塞主要关注的是I/O操作或系统调用的行为。阻塞(Blocking):在阻塞操作中,如果数据还没有准备好(例如,等待数据从磁盘读取或从网络接收),则调用者(通常是一个线程或进程)会被阻塞,直到数据准备好为止。在此期间,调用者无法执行其他任务,只能等待I/O操作完成。阻塞I/O操作的典型例子是普通的文件读写。非阻塞(Non-blocking):在非阻塞操作中,如果数据还没有准备好,调用者不会被阻塞,而是立即返回一个错误码(例如,表示资源不可用)。

weixin_44545838的博客 6410

【计算机网络】poll | epoll

第三种 小于0等待失败 返回-1如:想要等待下标为1 和2的文件描述符,但是下标为2的文件描述符根本不存在,就会等待失败。作为就绪队列的节点,需要包含文件描述符fd 以及 revent (操作系统告诉用户 关心的文件描述符的那些事件就绪)等待失败 返回-1如:想要等待下标为1 和2的文件描述符,但是下标为2的文件描述符根本不存在,就会等待失败。当特定的文件描述符上有对应的事件发生了,就可以将对应已经发生事件的节点 链入就绪队列中。(每次都需要内核需要告诉用户,关心的文件文件描述符上的那些事件就绪)

qq_62939852的博客 3273

C++ IO多路复用 poll模型

more。

qq_58539881的博客 764

poll()函数详解

poll提供的功能与select类似,不过在处理流设备时,它能够提供额外的信息。   #include   int poll(struct pollfd fd[], nfds_t nfds, int timeout);     参数:    1)第一个参数:一个结构数组,struct pollfd结构如下:   struct pollfd{   int fd;

djinglan的专栏 10万+

IO多路复用poll

本文讲解了poll多路复用,详细的讲解了poll函数,对它的参数、功能和优缺点等都做了分析。

qq_52836452的博客 800

C++网络编程I/O 多路复用poll

学到这,感觉没什么可说的,poll 是对select 的改进,但是它是个半成品,相对 select 提升不大。更多的是把poll当成一个过渡,大家并没有给poll过多的关注,而是直接epoll了。所以着重解释了从select到poll改变了什么,思路的变化,到如何使用上其实就是调函数,也没有太多细节要注意。上节通过解释和代码,我们可以总结select的缺点主要有:解决了select的两件事情:poll也是操作系统提供的系统调用函数,机制与select类似,管理多个描述符也是进行轮询,即每次调用都要扫描整个注

踩坑博客 3078

POLL机制示例代码

代码POLL机制示例代码

m0_72647108的博客 393

IO多路复用——select、poll、epoll

异步IO的优化思路是为了提升应用程序的效率。发送询问请求:应用程序先向内核询问是否有数据可用,这个步骤是阻塞的,程序会等待内核的响应。发送接收数据请求:一旦确认有数据,应用程序再向内核请求接收数据。这样的流程会导致不必要的等待,降低了程序的性能。而在异步IO模式下,应用程序只需向内核发送一次请求。这个请求同时包含了状态查询和数据拷贝的操作,内核处理完成后会直接通知应用程序。这样,应用程序不必在两个步骤之间来回切换,从而减少了等待时间,提高了整体效率。这种方式特别适合处理大量并发IO操作的场景。

m0_63086198的博客 1446

IO多路复用之select

IO多路复用也称IO多路转接,它是一种网络通信的手段(机制),通过这种方式可以同时监测多个文件描述符并且这个过程是阻塞的,一旦检测到有文件描述符就绪( 可以读数据或者可以写数据)程序的阻塞就会被解除,之后就可以基于这些(一个或多个)就绪的文件描述符进行通信了。通过这种方式在单线程/进程的场景下也可以在服务器端实现并发。常见的IO多路转接方式有:select、poll、epoll

qq_55882840的博客 3488

网络IOIO多路复用Select、Poll、ePoll

selectpoll和epoll都是针对IO的处理IO事件触发selectpoll和epoll是 Linux 系统中用于处理 I/O 多路复用的三种机制。它们允许一个进程监视多个文件描述符(通常是套接字),以查看它们是否准备好进行读、写或其他操作,而无需为每个文件描述符创建单独的线程或进程select和poll:都是基于轮询的方式来实现I/O多路复用,时间复杂度为O(n),且都需要将整个文件描述符集合从用户空间复制到内核空间,造成不必要的开销。它们的主要区别在于poll没有文件描述符数量的限制。epoll

m0_63428711的博客 2081

I/O多路复用模型之 select、poll、epoll

Redis 作为一个 Client-Server 架构的数据库,其源码中少不了用来实现网络通信的部分。而你应该也清楚,通常系统实现网络通信的基本方法是使用 Socket 编程模型,**包括创建 Socket、监听端口、处理连接请求和读写请求**。 但是,由于基本的 **Socket 编程模型一次只能处理一个客户端连接上的请求**,所以当要处理高并发请求时,一种方案就是使用多线程,让每个线程负责处理一个客户端的请求。 而 Redis 负责客户端请求解析和处理的线程只有一个,那么如果直接采用基本 Socke

持之以恒 7206

IO多路复用,select、poll、epoll总结

IO多路复用,select,poll,epoll总结

weixin_74920684的博客 935

IO多路复用的select, poll, epoll之间的区别和联系总结

IO多路复用 select, poll和epoll都是IO多路复用模型,所以在深入了解这三个系统调用之前,需要先简单介绍一下IO多路复用IO多路复用是一种复用技术,复用(multiplexing)技术很普遍,例如通信中有多路时分复用(OFDM)、频分复用、码分复用。再例如我们一个办公室的人可以共用办公室的一台打印机,大家在不同的时段使用即可实现复用,这就是时分复用。 “IO多路复用”是指复用了同一个处理线程(在Java中被抽象为选择器selector),由操作系统进行托管,当与选择器绑定的socket

技术笔记 6502

网络通信IO多路复用

网络IO通信涉及到硬件网卡以及操作系统内核态与用户态。其中网卡与内核态数据交互主要是通过DMA来进行传输并且由内核进行控制。用户态不需要感知网卡数据的情况,用户态主要是需要知晓内核态socket缓冲区数据是否就绪、并且将数据复制到用户缓冲区。IO模型是指用户态和内核态对IO数据交互处理方式。模型共有5种方式,可以根据连接数量和系统资源进行IO模型的选择,选择合适的IO模型可以加快系统效率节约资源。

wlphlj的专栏 1372

【Linux:IO多路复用(select、poll函数)

nfds:是在这三个文件描述符集合中找出一个最大的文件描述符再+1内核中需要线性遍历这些集合中的文件描述符,该值是循环结束的条件该参数在windows中无效,指定为-1即可readfds:读集合一般都是需要进行检测的,这样才能知道哪些文件描述符可以接收数据writefds:内核只检测这个集合中的文件描述符对应的写缓冲区,如果不使用该参数可以用NULL指定exceptfds:内核只检测这个集合中的文件描述符是否有异常状态,如果不使用该参数可以用NULL指定。

2201_75755162的博客 2185

网络io多路复用:select/poll/epoll详解

本文详细介绍了三种网络I/O多路复用技术:select、poll和epoll。首先分析了一请求一线程方式的弊端,指出多路复用技术可以有效解决高并发问题。select通过比特位集合管理文件描述符,但存在参数复杂、效率低下等缺点;poll采用结构体数组pollfd,比select更灵活高效;epoll则是Linux内核的重大改进,通过事件触发机制显著提升性能。文章分别展示了三种技术的代码实现,对比了它们的工作原理和优缺点,为开发高并发网络应用提供了技术参考。

adagio9z的博客 1875

【C语言IO多路复用 poll

【C语言IO多路复用 pollIO多路复用介绍poll介绍poll 相关函数介绍poll 代码使用结尾IO多路复用介绍 IO多路复用是一种实现高效并发的技术,在处理大量并发连接时非常有用。它可以通过同时监听多个文件描述符(sockets或文件),在有活动的文件描述符上进行读写操作,实现同时处理多个连接的能力。 ...

qq_35485206的博客 1048

select、poll、epoll三种IO多路复用的执行原理

另外poll的第二个参数有两种赋值方式,但是都和第一个参数的数组有关系:1.使用参数1数组的元素个数2. 使用参数1数组中存储的最后一个有效元素对应的下标值 + 1timeout:

mrmrmr666的博客 1207
上一篇: c++ bitset使用方法
下一篇: IO多路复用-epoll
Liuuuu408
博客等级 码龄5年 828粉丝 · 109原创
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值