/*
 * 02_echo_server.c —— select 版多客户端 echo 服务器
 *
 * 编译: gcc 02_echo_server.c -o echo_srv
 * 运行: ./echo_srv 9999
 * 测试: 开多个终端 `telnet localhost 9999` 或 `nc localhost 9999`
 *
 * 这个例子完整展示了 select 写服务器的标准套路：
 *   1) master / working 两份 fd_set
 *   2) 每次循环 working = master
 *   3) 遍历 0 ~ max_fd 检查就绪
 *   4) accept 新连接时维护 max_fd
 */

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

#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) {
        perror("bind"); exit(1);
    }
    if (listen(fd, 64) < 0) {
        perror("listen"); exit(1);
    }
    printf("🚀 echo 服务器已启动，端口 %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);

    fd_set master, working;
    FD_ZERO(&master);
    FD_SET(listen_fd, &master);
    int max_fd = listen_fd;

    while (1) {
        working = master;       /* select 会改写 working，必须每轮拷贝 */

        int n = select(max_fd + 1, &working, NULL, NULL, NULL);
        if (n < 0) {
            if (errno == EINTR) continue;
            perror("select");
            break;
        }

        for (int fd = 0; fd <= max_fd && n > 0; fd++) {
            if (!FD_ISSET(fd, &working)) continue;
            n--;

            if (fd == listen_fd) {
                /* 新连接到达 */
                struct sockaddr_in cli;
                socklen_t cli_len = sizeof(cli);
                int conn = accept(listen_fd, (struct sockaddr *)&cli, &cli_len);
                if (conn < 0) { perror("accept"); continue; }

                FD_SET(conn, &master);
                if (conn > max_fd) max_fd = conn;

                printf("✅ 新客户端 fd=%d, 来自 %s:%d (当前 max_fd=%d)\n",
                       conn, inet_ntoa(cli.sin_addr), ntohs(cli.sin_port), max_fd);
            } else {
                /* 已连接客户端有数据 */
                char buf[BUF_SIZE];
                ssize_t r = read(fd, buf, sizeof(buf));
                if (r <= 0) {
                    if (r == 0) printf("👋 客户端 fd=%d 断开\n", fd);
                    else perror("read");
                    close(fd);
                    FD_CLR(fd, &master);
                } else {
                    /* echo 回去 */
                    write(fd, buf, r);
                    printf("📨 fd=%d 收到 %ld 字节，已回 echo\n", fd, r);
                }
            }
        }
    }

    close(listen_fd);
    return 0;
}
