tinywebserver epoller模块 非阻塞IO

面试的时候,老是考阻塞IO和非阻塞IO的区别,其实在写代码的层面上,都是系统调用。这段代码摘自,非常的优雅,用了c++新特性,我倒是觉着,这些都是文档,到时候调他的包就好了,不懂再查。

tinywebserver

 上代码

接口

epoller.h

#pragma once
#include <vector>
#include <sys/epoll.h>
#include <fcntl.h>  // fcntl()
#include <unistd.h> // close()
#include <assert.h> // close()
#include <vector>
#include <errno.h>
class Epoller {
public:
    explicit Epoller(int maxEvent = 1024);

    ~Epoller();

    bool AddFd(int fd, uint32_t events);

    bool ModFd(int fd, uint32_t events);

    bool DelFd(int fd);

    int Wait(int timeoutMs = -1);

    int GetEventFd(size_t i) const;

    uint32_t GetEvents(size_t i) const;
        
private:
    int epollFd_;

    std::vector<struct epoll_event> events_;    
};

epoller.cpp   

#include "epoller.h"

Epoller::Epoller(int maxEvent):epollFd_(epoll_create(512)), events_(maxEvent){
    assert(epollFd_ >= 0 && events_.size() > 0);
}

Epoller::~Epoller() {
    close(epollFd_);
}

bool Epoller::AddFd(int fd, uint32_t events) {
    if(fd < 0) return false;
    epoll_event ev = {0};
    ev.data.fd = fd;
    ev.events = events;
    return 0 == epoll_ctl(epollFd_, EPOLL_CTL_ADD, fd, &ev);
}

bool Epoller::ModFd(int fd, uint32_t events) {
    if(fd < 0) return false;
    epoll_event ev = {0};
    ev.data.fd = fd;
    ev.events = events;
    return 0 == epoll_ctl(epollFd_, EPOLL_CTL_MOD, fd, &ev);
}

bool Epoller::DelFd(int fd) {
    if(fd < 0) return false;
    epoll_event ev = {0};
    return 0 == epoll_ctl(epollFd_, EPOLL_CTL_DEL, fd, &ev);
}

int Epoller::Wait(int timeoutMs) {
    return epoll_wait(epollFd_, &events_[0], static_cast<int>(events_.size()), timeoutMs);
}

int Epoller::GetEventFd(size_t i) const {
    assert(i < events_.size() && i >= 0);
    return events_[i].data.fd;
}

uint32_t Epoller::GetEvents(size_t i) const {
    assert(i < events_.size() && i >= 0);
    return events_[i].events;
}

这些接口就是在维护一个接口,将你需要监听的句柄存放在vector里,没有事件,epoller.wait会等待,有事件,epoller.wait返回。

例子

这里maim.cpp 举了一个例子。

#include <iostream>
#include <string>
#include <vector>
#include <cstring>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <sys/epoll.h>
#include <fcntl.h>
#include "epoller.h"

using namespace std;
#define REDIS_DEFAULT_PORT 6379


class RedisServer {
public:
    RedisServer() : epoll_(1024), listen_fd_(-1) {
        listen_fd_ = socket(AF_INET, SOCK_STREAM, 0);
        if (listen_fd_ == -1) {
            perror("socket creation failed");
            exit(EXIT_FAILURE);
        }

        struct sockaddr_in serv_addr;
        serv_addr.sin_family = AF_INET;
        serv_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); // 只监听本地
        serv_addr.sin_port = htons(REDIS_DEFAULT_PORT);

        if (bind(listen_fd_, (struct sockaddr*)&serv_addr, sizeof(serv_addr)) == -1) {
            perror("bind failed");  
            exit(EXIT_FAILURE);
        }

        if (listen(listen_fd_, SOMAXCONN) == -1) {
            perror("listen failed");
            exit(EXIT_FAILURE);
        }

        epoll_.AddFd(listen_fd_, EPOLLIN);
        fcntl(listen_fd_, F_SETFL, fcntl(listen_fd_, F_GETFD, 0) | O_NONBLOCK);
    }

    ~RedisServer() {
        close(listen_fd_);
    }

    void Run() {
        while (true) {
            int event_count = epoll_.Wait();
            for (int i = 0; i < event_count; ++i) {
                int fd = epoll_.GetEventFd(i);
                uint32_t events = epoll_.GetEvents(i);

                if (fd == listen_fd_) {
                    AcceptConnection();
                }else if (events &  (EPOLLRDHUP | EPOLLHUP | EPOLLERR)) {
                    CloseConnection(fd);
                }
                else if (events & EPOLLIN) {
                    HandleRequest(fd);
                } 
            }
        }
    }

private:
    void CloseConnection(int fd) {
        cout << "Client[" << fd << "] disconnected!" << endl;
        close(fd);
        epoll_.DelFd(fd);
    }
    void AcceptConnection() {
        struct sockaddr_in cli_addr;
        socklen_t cli_addr_len = sizeof(cli_addr);
        int conn_fd = accept(listen_fd_, (struct sockaddr*)&cli_addr, &cli_addr_len);
        if (conn_fd == -1) {
            perror("accept failed");
            return;
        }

        std::cout << "Accepted new connection" << std::endl;
        epoll_.AddFd(conn_fd, EPOLLIN);
    }

    void HandleRequest(int fd) {
        char buffer[1024];
        ssize_t bytes_read = recv(fd, buffer, sizeof(buffer), 0);
        if (bytes_read == -1) {
            perror("recv failed");
            return;
        }

        std::cout << "Received request: " << std::string(buffer, bytes_read) << std::endl;

        const char* response = "+OK\r\n";
        ssize_t bytes_written = send(fd, response, strlen(response), 0);
        if (bytes_written == -1) {
            perror("send failed");
            return;
        }
    }

    Epoller epoll_;
    int listen_fd_;
};

int main(){
    RedisServer server;
    server.Run();
    return 0;
}

这里是建立了一个socket,监听本地6379端口,运行之后,可以在终端中输入,redis-cli INFO 、redis-cli PING发送数据,这边会打引出来。

这个模块,只需要写while循环里写对应的函数就可以了,比方说接收到连接的时候添加到vector里去,接收到请求的时候调用对应的处理函数。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值