/*
 * 02_echo_server.c —— poll 版多客户端 echo 服务器
 *
 * 编译: gcc 02_echo_server.c -o poll_srv
 * 运行: ./poll_srv 9999
 * 测试: nc localhost 9999  (开多个终端体验同时连接)
 *
 * 与 select 版相比，主要差异：
 *   1) 没有 master/working 双份（events 不会被改写）
 *   2) 用 pollfd 数组管理，紧凑结构
 *   3) 没有 1024 上限
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>

#define MAX_CLIENTS 1024
#define BUF_SIZE    1024

static int make_listen_socket(int port) {
    int fd = socket(AF_INET, SOCK_STREAM, 0);
    if (fd < 0) { perror("socket"); exit(1); }

    int yes = 1;
    setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes));

    struct sockaddr_in addr = {0};
    addr.sin_family      = AF_INET;
    addr.sin_addr.s_addr = htonl(INADDR_ANY);
    addr.sin_port        = htons(port);

    if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0 ||
        listen(fd, 64) < 0) {
        perror("bind/listen"); exit(1);
    }
    printf("🚀 poll 服务器已启动，端口 %d\n", port);
    return fd;
}

int main(int argc, char *argv[]) {
    int port = (argc > 1) ? atoi(argv[1]) : 9999;
    int listen_fd = make_listen_socket(port);

    struct pollfd pfds[MAX_CLIENTS + 1];
    pfds[0].fd = listen_fd;
    pfds[0].events = POLLIN;
    int nfds = 1;

    while (1) {
        int n = poll(pfds, nfds, -1);     /* -1 = 永久阻塞 */
        if (n < 0) {
            if (errno == EINTR) continue;
            perror("poll");
            break;
        }

        /* 1) 处理新连接 */
        if (pfds[0].revents & POLLIN) {
            struct sockaddr_in cli;
            socklen_t cli_len = sizeof(cli);
            int conn = accept(listen_fd, (struct sockaddr *)&cli, &cli_len);
            if (conn >= 0 && nfds < MAX_CLIENTS + 1) {
                pfds[nfds].fd = conn;
                pfds[nfds].events = POLLIN | POLLRDHUP;   /* POLLRDHUP 是 Linux 扩展 */
                pfds[nfds].revents = 0;
                nfds++;
                printf("✅ 新客户端 fd=%d, 来自 %s:%d (当前在线 %d)\n",
                       conn, inet_ntoa(cli.sin_addr), ntohs(cli.sin_port), nfds - 1);
            } else {
                printf("⚠️ 连接被拒：accept 失败或槽位满\n");
                if (conn >= 0) close(conn);
            }
        }

        /* 2) 处理已连接客户端（注意删除时不要 i++） */
        for (int i = 1; i < nfds; ) {
            short re = pfds[i].revents;
            if (re == 0) { i++; continue; }

            int closed = 0;

            if (re & POLLIN) {
                char buf[BUF_SIZE];
                ssize_t r = read(pfds[i].fd, buf, sizeof(buf));
                if (r <= 0) {
                    closed = 1;
                    if (r == 0) printf("👋 fd=%d 正常断开\n", pfds[i].fd);
                    else        perror("read");
                } else {
                    write(pfds[i].fd, buf, r);
                    printf("📨 fd=%d 收到 %ld 字节，已 echo\n", pfds[i].fd, r);
                }
            }
            if (re & (POLLERR | POLLHUP | POLLRDHUP | POLLNVAL)) {
                printf("⚠️ fd=%d 触发 ERR/HUP\n", pfds[i].fd);
                closed = 1;
            }

            if (closed) {
                close(pfds[i].fd);
                pfds[i] = pfds[--nfds];   /* 末尾填空，紧凑数组 */
                /* 不 i++，下一轮重新检查 i 位置 */
            } else {
                i++;
            }
        }
    }
    close(listen_fd);
    return 0;
}
