IO多路复用之poll

poll

在指定的时间内,轮询一定数量的文件描述符(该文件描述符存放在一个数组)

#include <poll.h>
int po1l(struct pollfd* fds, nfds_t nfds, int timeout);

// ---------------------------
- fds 参数是一个pollfd结构类型的数组,它指定所有我们感兴趣的文件描述符上发生的可读、可写和异常等事件。
- nfds参数指定被监听事件集合fds的大小。其类型nfds_t的定义如下: typedef unsigned 1ong int nfds_t 
- timeout 参数指定poll的超时值,单位是毫秒。当timeout为-1时,poll调用将永远阻塞,直到某个事件发生:当timecout为0时,poll调用将立即返回。
// pollfd 结构体的定义如下:
struct pollfd{
    int fd;  		/* 文件描述符 */
    short events;  	/* 注册的事件 */
    short revents; 	/*实际发生的事件,由内核填充*/
};

// --------------------------
- fd成员指定文件描述符:
- events 成员告诉poll监听fd上的哪些事件,它是一系列事件的按位或; 
- revents 成员则由内核修改,以通知应用程序fd上实际发生了哪些事件(设置和select的区别)

poll系统调用的返回值的含义与select相同。

代码案例

服务端功能 : 将接收的客户端的数据逆转发送给所有连接的客户端

int main(){

    // 1. 创建服务端socket
    int server_socket = socket(AF_INET, SOCK_STREAM, 0);
    if(server_socket == -1){
        perror("Socket creation failed");
        exit(EXIT_FAILURE);
    }

    // 2. 设置端口复用
    int opt = 1;
    if(setsockopt(server_socket, SOL_SOCKET, SO_REUSEPORT, &opt, sizeof(opt)) == -1){
        perror("socket opt failed");
        exit(EXIT_FAILURE);
    }

    // 3. 设置服务器端口号和ip
    struct sockaddr_in server_addr;
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(8080);

    // 4. 绑定ip和端口号
    if(bind(server_socket, (struct sockaddr*)&server_addr, sizeof(server_addr)) == -1){
        perror("binding failed");
        exit(EXIT_FAILURE);
    }

    // 5. 监听
    if(listen(server_socket, 5) == -1){
        perror("listening failed");
        exit(EXIT_FAILURE);
    }

    // 6. 设置pollfd事件集(是一个数组,数组中的每个元素是pollfd结构体)
    std::vector<pollfd> pollFds(1);
    pollFds[0].fd = server_socket;
    pollFds[0].events = POLLIN;     //  可读事件

    std::vector<int> clientSockets; //  存放所有客户端的socket

    while(true){

        int result = poll(pollFds.data(), pollFds.size(), -1);  
        if(result == -1){
             perror("poll failed");
            exit(EXIT_FAILURE);
        }   

        for(const auto &pollFd : pollFds){
            
            // 如果该事件是可读事件
            if(pollFd.revents & POLLIN){

                // 事件可读且是服务端socket时
                if(pollFd.fd == server_socket){

                    // 创建新的客户端连接
                    struct sockaddr_in client_addr;
                    socklen_t client_len = sizeof(client_addr);
                    int client_socket = accept(server_socket, (struct sockaddr*)&client_addr, &client_len);
                    if (client_socket == -1) {
                        perror("accept failed");
                        exit(EXIT_FAILURE);
                    }

                    std::cout<<"accept new connection id is : ["<<client_socket<<"]"<<std::endl;

                    // 给该连接的客户端设置事件,并放入pollfds中
                    pollfd newClient;
                    newClient.fd = client_socket;
                    newClient.events = POLLIN;
                    pollFds.push_back(newClient);

                    // 存放新连接的客户端socket
                    clientSockets.push_back(client_socket);

                    
                }
                // 如果时客户端socket(文件fd),且时可读事件时
                else{
                    //  读取客户端数据
                    char buffer[2048];
                    ssize_t byteRead = recv(pollFd.fd, buffer, sizeof(buffer)-1, 0);

                    if(byteRead <= 0){
                        // 客户端连接出错
                        std::cout << "Client disconnected" << std::endl;
                        close(pollFd.fd);
                        pollFds.erase(std::remove_if(
                                            pollFds.begin(), pollFds.end(), [pollFd](const pollfd& fd){
                                                                return fd.fd == pollFd.fd;
                                                            }
                                            ),
                                        pollFds.end()
                            );
                        clientSockets.erase(std::remove(
                                                clientSockets.begin(), clientSockets.end(), pollFd.fd), 
                                            clientSockets.end()
                            );
                        
                    }
                    else{
                        // 接收数据
                        buffer[byteRead] = '\0';
                        std::cout<<"receive from ["<<pollFd.fd<<"]"<<" data is : "<<buffer<<std::endl;

                        // 逆转数据,并返回(注意:poll的pollfd被内核修改revents,而events没有被修改)
                        std::string s(buffer);
                        std::reverse(s.begin(), s.end());
                        for(const auto &client : clientSockets){
                            std::cout<<"["<<server_socket<<"] send data to "<<"["<<client<<"]"<<std::endl;
                            send(client, s.data(), s.size(), 0);
                        }
                    }
                }
            }
        }


    }
    close(server_socket);
    return 0;
}
  • 7
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值