使用epoll实现服务器并发

io多路复用一共有三种方式select poll epoll 由于poll用的比较少,博主就没有介绍,epoll是三种方式中用的最多的一种,由于它的底层是有红黑树实现的再加上它独特的回调机制使得它在处理并发量较高的场景中具有明显优势。接下来是使用epoll实现服务器并发的代码演示,代码具有大量注释,可以很容易看懂。

include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#define PORT 8888
using namespace std;

mutex mtx;

// 定义一个结构体来存储相关信息
typedef struct infor {
    epoll_event *ev;
    int epct;
    int epfd;
    int fd;
} infor;

// 处理新连接的函数
void listen_handler(infor info) {
    int cfd = accept(info.fd, NULL, NULL);  // 接受连接
    if (cfd == -1) {
        cout << "accept error" << endl;
        return;
    }

    epoll_event ev;
    ev.events = EPOLLIN;  // 关注读事件
    ev.data.fd = cfd;  // 关联客户端套接字
    unique_lock lock(mtx);
    lock.lock();
    int epct = epoll_ctl(info.epfd, EPOLL_CTL_ADD, cfd, &ev);  // 将客户端套接字加入epoll监控
    if (epct == -1) {
        cout << "epoll_ctl add client fd error" << endl;
        close(cfd);  // 如果出错,关闭客户端套接字
    }
}

// 处理客户端请求的函数
void client_handler(infor info) {
    char buf[1024];
    int len = recv(info.fd, buf, sizeof(buf), 0);  // 接收数据
    if (len == -1) {
        cout << "recv error" << endl;
    } else if (len == 0) {  // 客户端关闭连接
        unique_lock lock(mtx);
        lock.lock();
        epoll_ctl(info.epfd, EPOLL_CTL_DEL, info.fd, NULL);  // 从epoll监控中移除
close(info.fd);  // 关闭客户端套接字
        cout << "client is closed" << endl;
    } else {  // 客户端发送数据
        for (int j = 0; j < len; j++) {  // 将数据转换为大写
            buf[j] = toupper(buf[j]);
        }
        int ret = send(info.fd, buf, len, 0);  // 发送数据
        if (ret == -1) {
            cout << "send error" << endl;
        }
    }
}

int main() {
    int lfd = socket(AF_INET, SOCK_STREAM, 0);  // 创建套接字
    if (lfd == -1) {
        cout << "socket error" << endl;
        return -1;
    }

    struct sockaddr_in serv_addr;  // 服务器地址结构
    memset(&serv_addr, 0, sizeof(serv_addr));  // 初始化地址结构
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(PORT);  // 设置端口号
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);

    int optval = 1;
    setsockopt(lfd, SOL_SOCKET, SO_REUSEADDR, &optval, sizeof(optval));  // 设置套接字选项

    // 绑定地址
    int ret = bind(lfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if (ret == -1) {
        cout << "bind error" << endl;
        close(lfd);
        return -1;
    }

    ret = listen(lfd, 128);  // 监听套接字
    if (ret == -1) {
        cout << "listen error" << endl;
        close(lfd);
        return -1;
    }

    int epfd = epoll_create(1);  // 创建epoll实例
    if (epfd == -1) {
        cout << "epoll create error" << endl;
        close(lfd);
        return -1;
    }

    epoll_event ev;  // epoll事件结构
    ev.events = EPOLLIN;  // 关注读事件
    ev.data.fd = lfd;  // 关联监听套接字
ret = epoll_ctl(epfd, EPOLL_CTL_ADD, lfd, &ev);
    if (ret == -1) {
        cout << "epoll_ctl add listen fd error" << endl;
        close(lfd);
        close(epfd);
        return -1;
    }

    vector evs(1024);  // 初始化epoll事件数组,分配足够空间
    while (1) {
        int number = epoll_wait(epfd, evs.data(), evs.size(), -1);  // 等待epoll事件
        if (number == -1) {
            cout << "epoll_wait error" << endl;
            continue;
        }

        for (int i = 0; i < number; ++i) {  // 遍历触发的事件
            int fd = evs[i].data.fd;
            if (fd == lfd) {
                // 如果是监听套接字
                infor info;
                info.ev = &ev;
                info.epct = ret;
                info.epfd = epfd;
                info.fd = lfd;
                thread t(listen_handler, info);  // 创建线程处理新连接
                t.detach();  // 分离线程
            } else {
                // 如果是客户端套接字
                infor info;
                info.ev = &ev;
                info.epct = ret;
                info.epfd = epfd;
                info.fd = fd;
                thread t(client_handler, info);  // 创建线程处理客户端请求
                t.detach();  // 分离线程
            }
        }
    }

    close(lfd);  // 关闭监听套接字
    close(epfd);  // 关闭epoll实例
    return 0;

                             

它的作用依旧是将大写转化为小写,支持并发量较高的场景。

今天的更新就到这里了,大家再见!!!