epoll 在多线程环境中实现并发服务器的例子

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

// 定义常量
#define PORT 8001
#define MAX_EVENTS 10
#define MAX_THREADS 4

// 全局变量
int listen_fd, epoll_fd;
pthread_t threads[MAX_THREADS];
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;

// 线程函数,用来处理客户端连接
void* handle_connection(void* arg) {
    int thread_id = *(int*)arg;
    struct epoll_event events[MAX_EVENTS];
    int event_count, client_fd;

    while (1) {
        // 使用互斥锁来保证线程安全
        pthread_mutex_lock(&mutex);

        // 等待文件描述符上的事件
        event_count = epoll_wait(epoll_fd, events, MAX_EVENTS, -1);
        if (event_count == -1) {
            perror("epoll_wait");
            pthread_exit(NULL);
        }

        // 遍历事件数组,处理每一个事件
        for (int i = 0; i < event_count; i++) {
            // 如果是监听套接字上的读事件,表示有新客户端连接
            if (events[i].data.fd == listen_fd) {
                // 接受客户端连接
                client_fd = accept(listen_fd, NULL, NULL);
                if (client_fd == -1) {
                    perror("accept");
                } else {
                    // 将客户端套接字添加到 epoll 实例中
                    struct epoll_event event;
                    event.events = EPOLLIN;
                    event.data.fd = client_fd;
                    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &event) == -1) {
                        perror("epoll_ctl");
                        close(client_fd);
                    } else {
                        printf("Thread %d: Accepted new client connection (fd=%d)\n", thread_id, client_fd);
                    }
                }
            } else {
                // 否则就是客户端套接字上的读事件
                client_fd = events[i].data.fd;
                printf("Thread %d: Handling client connection (fd=%d)\n", thread_id, client_fd);

                // 处理客户端数据
                // ...

                // 从 epoll 实例中删除客户端套接字
                struct epoll_event event;
                event.events = EPOLLIN;
                event.data.fd = client_fd;
                if (epoll_ctl(epoll_fd, EPOLL_CTL_DEL, client_fd, &event) == -1) {
                    perror("epoll_ctl");
                }
                close(client_fd);
                printf("Thread %d: Closed client connection (fd=%d)\n", thread_id, client_fd);
            }
        }

        pthread_mutex_unlock(&mutex);
    }

    pthread_exit(NULL);
}

// 主函数,创建监听套接字和 epoll 实例
int main() {
    struct sockaddr_in addr;
    socklen_t addrlen = sizeof(addr);

    // 创建监听套接字,并绑定到指定端口上
    listen_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (listen_fd == -1) {
        perror("socket");
        exit(EXIT_FAILURE);
    }

    memset(&addr, 0, sizeof(addr));
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr =_port = htons(PORT);

    if (bind(listen_fd, (struct sockaddr*)&addr, addrlen) == -1) {
        perror("bind");
        exit(EXIT_FAILURE);
    }

    // 开始监听端口
    if (listen(listen_fd, SOMAXCONN) == -1) {
        perror("listen");
        exit(EXIT_FAILURE);
    }

    // 创建 epoll 实例并添加监听套接字到其中
    epoll_fd = epoll_create(MAX_EVENTS);
    if (epoll_fd == -1) {
        perror("epoll_create");
        exit(EXIT_FAILURE);
    }

    struct epoll_event event;
    event.events = EPOLLIN;
    event.data.fd = listen_fd;

    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, listen_fd, &event) == -1) {
        perror("epoll_ctl");
        exit(EXIT_FAILURE);
    }

    // 创建多个线程
    int thread_ids[MAX_THREADS];
    for (int i = 0; i < MAX_THREADS; i++) {
        thread_ids[i] = i;
        pthread_create(&threads[i], NULL, handle_connection, &thread_ids[i]);
    }

    // 等待所有线程退出
    for (int i = 0; i < MAX_THREADS; i++) {
        pthread_join(threads[i], NULL);
    }

    // 关闭监听套接字和 epoll 实例
    close(listen_fd);
    close(epoll_fd);

    return 0;
}

在上述代码中,我们创建了一个监听套接字,通过这个套接字来接收客户端的连接请求。我们还创建了一个 epoll 实例,并将监听套接字添加到其中,用来监听多个文件描述符上的事件。为了支持并发处理客户端连接,我们使用多个线程来监听 epoll 实例并处理客户端请求。主线程创建了多个子线程,并且在这些线程之间共享 epoll 实例。

每个线程都使用 pthread_mutex_lock 和 pthread_mutex_unlock 来获取和释放互斥锁,以保证线程安全。线程首先调用 epoll_wait 来等待有事件发生,并处理每一个事件。

如果是监听套接字上的读事件,表示有新客户端连接。我们接受客户端连接,并将客户端套接字添加到 epoll 实例中,用来监听客户端套接字上的读事件。如果是客户端套接字上的读事件,表示客户端发送了数据过来,我们可以在线程中处理这些数据,然后从 epoll 实例中将客户端套接字删除。

在主函数中,我们创建了多个线程,并在这些线程之间共享了 epoll 实例。最后我们等待所有线程退出,并关闭监听套接字和 epoll 实例。

你可能感兴趣的:(服务器,c++,c语言)