epoll多线程IO处理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>

#define SERVER_PORT     8000
#define MAX_EVENTS      1024
#define BUFFER_SIZE     4096
#define THREAD_COUNT    4
#define QUEUE_SIZE      10000

// 任务结构体:传递给工作线程
typedef struct {
    int client_fd;
    struct sockaddr_in client_addr;
} task_t;

// 任务队列(线程安全)
task_t task_queue[QUEUE_SIZE];
int queue_head = 0;
int queue_tail = 0;
int queue_count = 0;
pthread_mutex_t queue_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t  queue_not_empty = PTHREAD_COND_INITIALIZER;
pthread_cond_t  queue_not_full  = PTHREAD_COND_INITIALIZER;

volatile int running = 1;  // 控制主循环

// 设置非阻塞
int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

// 信号处理:优雅关闭
void signal_handler(int sig) {
    printf("\n Received signal %d, shutting down...\n", sig);
    running = 0;
}


// 工作线程:从队列取任务处理
void* worker_routine(void* arg) {
    char buffer[BUFFER_SIZE];
    while (running) {
        task_t task;
        pthread_mutex_lock(&queue_mutex);
        while (queue_count == 0 && running) {
            pthread_cond_wait(&queue_not_empty, &queue_mutex);
        }
        if (!running) {
            pthread_mutex_unlock(&queue_mutex);
            break;
        }

        task = task_queue[queue_head];
        queue_head = (queue_head + 1) % QUEUE_SIZE;
        queue_count--;
        pthread_cond_signal(&queue_not_full);
        pthread_mutex_unlock(&queue_mutex);

        // 处理该客户端
        handle_connection(task.client_fd, &task.client_addr);
    }
    return NULL;
}

// 处理单个客户端连接(在工作线程中执行)
void handle_connection(int client_fd, struct sockaddr_in* client_addr) {
    ssize_t n;
    char buffer[BUFFER_SIZE];

    while ((n = recv(client_fd, buffer, sizeof(buffer), 0)) > 0) {
        buffer[n] = '\0';
        printf("[Worker %ld] Received %zd bytes from %s:%d: %s",
               pthread_self(), n, inet_ntoa(client_addr->sin_addr),
               ntohs(client_addr->sin_port), buffer);

        // 回显
        if (send(client_fd, buffer, n, 0) < 0) {
            perror("send failed");
            break;
        }
        printf("[Worker %ld] Sent %zd bytes back\n", pthread_self(), n);
    }

    if (n == 0) {
        printf("Client %s:%d (fd=%d) disconnected.\n",
               inet_ntoa(client_addr->sin_addr), ntohs(client_addr->sin_port), client_fd);
    } else if (n < 0) {
        if (errno != ECONNRESET) {
            perror("recv error");
        }
    }

    close(client_fd);
    printf("Closed client fd %d\n", client_fd);
}

int main() {
    signal(SIGINT, signal_handler);
    signal(SIGTERM, signal_handler);

    int server_fd, epoll_fd;
    struct epoll_event ev, events[MAX_EVENTS];

    // 创建监听 socket
    server_fd = socket(AF_INET, SOCK_STREAM, 0);
    if (server_fd < 0) {
        perror("socket failed");
        exit(EXIT_FAILURE);
    }

    // 设置端口复用
    int opt = 1;
    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));

    // 绑定
    struct sockaddr_in server_addr;
    memset(&server_addr, 0, sizeof(server_addr));
    server_addr.sin_family = AF_INET;
    server_addr.sin_addr.s_addr = INADDR_ANY;
    server_addr.sin_port = htons(SERVER_PORT);

    if (bind(server_fd, (struct sockaddr*)&server_addr, sizeof(server_addr)) < 0) {
        perror("bind failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    if (listen(server_fd, SOMAXCONN) < 0) {
        perror("listen failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    set_nonblocking(server_fd);

    // 创建 epoll 实例
    epoll_fd = epoll_create1(EPOLL_CLOEXEC);
    if (epoll_fd < 0) {
        perror("epoll_create1 failed");
        close(server_fd);
        exit(EXIT_FAILURE);
    }

    // 将监听 socket 加入 epoll(ET 模式)
    ev.events = EPOLLIN | EPOLLET;
    ev.data.fd = server_fd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0) {
        perror("epoll_ctl add server_fd failed");
        close(server_fd);
        close(epoll_fd);
        exit(EXIT_FAILURE);
    }

    // 创建线程池
    pthread_t workers[THREAD_COUNT];
    for (int i = 0; i < THREAD_COUNT; i++) {
        pthread_create(&workers[i], NULL, worker_routine, NULL);
    }

    printf("Multi-threaded epoll Server started on port %d\n", SERVER_PORT);
    printf("%d worker threads running...\n", THREAD_COUNT);

    // 主事件循环
    while (running) {
        int nfds = epoll_wait(epoll_fd, events, MAX_EVENTS, 1000); // 1s 超时,响应信号
        if (nfds < 0 && errno == EINTR) continue;
        if (nfds < 0) {
            perror("epoll_wait failed");
            break;
        }

        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == server_fd) {
                // 新连接到来
                struct sockaddr_in client_addr;
                socklen_t client_len = sizeof(client_addr);
                int client_fd;

                // ET 模式:必须循环 accept
                while ((client_fd = accept(server_fd, (struct sockaddr*)&client_addr, &client_len)) > 0) {
                    set_nonblocking(client_fd);
                    printf("Accepted client: %s:%d (fd=%d)\n",
                           inet_ntoa(client_addr.sin_addr), ntohs(client_addr.sin_port), client_fd);

                    // 创建任务
                    task_t task;
                    task.client_fd = client_fd;
                    memcpy(&task.client_addr, &client_addr, sizeof(client_addr));

                    // 加入任务队列
                    pthread_mutex_lock(&queue_mutex);
                    while (queue_count >= QUEUE_SIZE && running) {
                        pthread_cond_wait(&queue_not_full, &queue_mutex);
                    }
                    if (!running) {
                        pthread_mutex_unlock(&queue_mutex);
                        close(client_fd);
                        break;
                    }
                    task_queue[queue_tail] = task;
                    queue_tail = (queue_tail + 1) % QUEUE_SIZE;
                    queue_count++;
                    pthread_cond_signal(&queue_not_empty);
                    pthread_mutex_unlock(&queue_mutex);
                }

                if (errno != EAGAIN) {
                    perror("accept error");
                }
            }
        }
    }

    printf("\nShutting down server...\n");

    // 等待所有线程结束
    for (int i = 0; i < THREAD_COUNT; i++) {
        pthread_join(workers[i], NULL);
    }

    close(server_fd);
    close(epoll_fd);
    printf("Server stopped.\n");
    return 0;
}
// filename: epoll_client_heartbeat.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <sys/epoll.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <time.h>

#define SERVER_IP         "127.0.0.1"
#define SERVER_PORT       8000
#define BUFFER_SIZE       4096
#define EPOLL_EVENTS      10
#define CONNECT_TIMEOUT   5     // 连接超时(秒)
#define HEARTBEAT_INTERVAL 10   // 心跳间隔(秒)
#define HEARTBEAT_TIMEOUT  3    // 最多允许失败次数

// 心跳消息
#define PING_MSG "PING\n"
#define PONG_MSG "PONG\n"
#define PING_LEN 5
#define PONG_LEN 5

// 客户端状态
typedef enum {
    CONNECTING,
    CONNECTED,
    DISCONNECTED
} client_state_t;

// 全局状态
client_state_t state = CONNECTING;
time_t last_heartbeat;           // 最后一次成功通信时间
time_t next_heartbeat;           // 下次应发送心跳时间
int heartbeat_failed_count = 0;  // 心跳失败次数

// 设置非阻塞
int set_nonblocking(int fd) {
    int flags = fcntl(fd, F_GETFL, 0);
    if (flags == -1) flags = 0;
    return fcntl(fd, F_SETFL, flags | O_NONBLOCK);
}

// 创建非阻塞 socket 并发起连接
int connect_nonblocking(const char* ip, int port) {
    int sockfd;
    struct sockaddr_in serv_addr;

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (sockfd < 0) {
        perror("socket creation failed");
        return -1;
    }

    set_nonblocking(sockfd);

    memset(&serv_addr, 0, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_port = htons(port);

    if (inet_pton(AF_INET, ip, &serv_addr.sin_addr) <= 0) {
        fprintf(stderr, "invalid address: %s\n", ip);
        close(sockfd);
        return -1;
    }

    int result = connect(sockfd, (struct sockaddr*)&serv_addr, sizeof(serv_addr));
    if (result < 0) {
        if (errno != EINPROGRESS) {
            perror("connect failed");
            close(sockfd);
            return -1;
        }
    }

    return sockfd;
}

// 发送心跳包
void send_heartbeat(int sockfd) {
    if (send(sockfd, PING_MSG, PING_LEN, 0) == PING_LEN) {
        printf("Sent heartbeat: PING\n");
    } else {
        perror("Failed to send heartbeat");
    }
}

// 处理接收的数据
void handle_read(int sockfd, char* buffer, ssize_t n) {
    if (n <= 0) return;

    buffer[n] = '\0';
    printf("Received: %s", buffer);

    // 更新最后通信时间
    last_heartbeat = time(NULL);

    // 检查是否是 PONG 响应
    if (n == PONG_LEN && strncmp(buffer, PONG_MSG, PONG_LEN) == 0) {
        printf("Heartbeat response received: PONG\n");
        heartbeat_failed_count = 0;  // 重置失败计数
    }
    // 其他业务消息...
}

// 检查心跳是否超时
int check_heartbeat_timeout(int sockfd) {
    time_t now = time(NULL);

    // 如果长时间无通信(收或发),发送心跳
    if (state == CONNECTED && now >= next_heartbeat) {
        send_heartbeat(sockfd);
        next_heartbeat = now + HEARTBEAT_INTERVAL;
        // 注意:我们不立即增加 failed_count,等下次检查时看是否收到 PONG
    }

    // 如果太久没收到任何响应(包括 PONG)
    if (state == CONNECTED && now - last_heartbeat > HEARTBEAT_INTERVAL * (HEARTBEAT_TIMEOUT + 1)) {
        heartbeat_failed_count = HEARTBEAT_TIMEOUT + 1;
    }

    if (heartbeat_failed_count >= HEARTBEAT_TIMEOUT) {
        fprintf(stderr, "Heartbeat failed %d times, connection lost.\n", heartbeat_failed_count);
        return -1; // 触发断开
    }

    return 0;
}

int main() {
    int sockfd, epoll_fd;
    struct epoll_event ev, events[EPOLL_EVENTS];
    char buffer[BUFFER_SIZE];
    ssize_t n;
    int done = 0;
    time_t connect_start;

    printf("Connecting to %s:%d...\n", SERVER_IP, SERVER_PORT);
    sockfd = connect_nonblocking(SERVER_IP, SERVER_PORT);
    if (sockfd < 0) {
        exit(EXIT_FAILURE);
    }

    epoll_fd = epoll_create1(0);
    if (epoll_fd < 0) {
        perror("epoll_create1 failed");
        close(sockfd);
        exit(EXIT_FAILURE);
    }

    ev.events = EPOLLOUT | EPOLLIN | EPOLLET;
    ev.data.fd = sockfd;
    if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, sockfd, &ev) < 0) {
        perror("epoll_ctl add failed");
        close(sockfd);
        close(epoll_fd);
        exit(EXIT_FAILURE);
    }

    // 初始化时间
    connect_start = time(NULL);
    last_heartbeat = time(NULL); // 刚连接,视为有通信
    next_heartbeat = last_heartbeat + HEARTBEAT_INTERVAL;

    while (!done) {
        // 计算 epoll_wait 超时时间(最多等待 1 秒,以便及时检查心跳)
        int timeout_ms = 1000;

        int nfds = epoll_wait(epoll_fd, events, EPOLL_EVENTS, timeout_ms);
        if (nfds < 0) {
            if (errno == EINTR) continue;
            perror("epoll_wait failed");
            break;
        }

        time_t now = time(NULL);

        // 处理事件
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == sockfd) {
                uint32_t events_mask = events[i].events;

                // 连接完成
                if (state == CONNECTING && (events_mask & EPOLLOUT)) {
                    int error = 0;
                    socklen_t len = sizeof(error);
                    if (getsockopt(sockfd, SOL_SOCKET, SO_ERROR, &error, &len) < 0 ||
                        error != 0) {
                        errno = error ? error : errno;
                        perror("connection failed");
                        done = 1;
                        break;
                    }

                    state = CONNECTED;
                    printf("Connected to server\n");

                    // 修改监听事件
                    ev.events = EPOLLIN | EPOLLET;
                    ev.data.fd = sockfd;
                    epoll_ctl(epoll_fd, EPOLL_CTL_MOD, sockfd, &ev);

                    // 发送欢迎消息
                    const char* hello = "Hello from client with heartbeat!\n";
                    send(sockfd, hello, strlen(hello), 0);
                    printf("Sent: %s", hello);
                }

                // 接收数据
                if (events_mask & EPOLLIN) {
                    while ((n = recv(sockfd, buffer, sizeof(buffer)-1, 0)) > 0) {
                        handle_read(sockfd, buffer, n);
                    }

                    if (n == 0) {
                        printf("Server disconnected.\n");
                        done = 1;
                    } else if (n < 0 && errno != EAGAIN && errno != EWOULDBLOCK) {
                        perror("recv error");
                        done = 1;
                    }
                }

                if (events_mask & (EPOLLERR | EPOLLHUP)) {
                    fprintf(stderr, "Socket error or hangup.\n");
                    done = 1;
                }
            }
        }

        // 超时检查:连接阶段
        if (state == CONNECTING && now - connect_start > CONNECT_TIMEOUT) {
            fprintf(stderr, "Connection timeout\n");
            done = 1;
        }

        // 检查心跳
        if (state == CONNECTED) {
            if (check_heartbeat_timeout(sockfd) < 0) {
                done = 1;
            }
        }
    }

    printf("Closing connection...\n");
    close(sockfd);
    close(epoll_fd);
    printf("Client exited.\n");
    return 0;
}
posted @ 2018-11-19 23:05  osbreak  阅读(303)  评论(0)    收藏  举报