tcp_echo_server.c

Download
c 139 lines 3.9 KB
  1/**
  2 * TCP Echo Server with select() multiplexing
  3 *
  4 * Handles multiple clients concurrently using select().
  5 * Each message received from a client is echoed back.
  6 *
  7 * Build: make
  8 * Usage: ./tcp_echo_server [port]
  9 *   Default port: 8080
 10 */
 11
 12#include <stdio.h>
 13#include <stdlib.h>
 14#include <string.h>
 15#include <unistd.h>
 16#include <sys/socket.h>
 17#include <sys/select.h>
 18#include <netinet/in.h>
 19#include <arpa/inet.h>
 20#include <errno.h>
 21
 22#define DEFAULT_PORT 8080
 23#define MAX_CLIENTS  10
 24#define BUF_SIZE     1024
 25
 26int main(int argc, char *argv[]) {
 27    int port = (argc > 1) ? atoi(argv[1]) : DEFAULT_PORT;
 28
 29    /* Create server socket */
 30    int server_fd = socket(AF_INET, SOCK_STREAM, 0);
 31    if (server_fd < 0) {
 32        perror("socket");
 33        exit(EXIT_FAILURE);
 34    }
 35
 36    /* Allow address reuse */
 37    int opt = 1;
 38    setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
 39
 40    /* Bind */
 41    struct sockaddr_in addr = {
 42        .sin_family = AF_INET,
 43        .sin_addr.s_addr = INADDR_ANY,
 44        .sin_port = htons(port)
 45    };
 46
 47    if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
 48        perror("bind");
 49        close(server_fd);
 50        exit(EXIT_FAILURE);
 51    }
 52
 53    /* Listen */
 54    if (listen(server_fd, 5) < 0) {
 55        perror("listen");
 56        close(server_fd);
 57        exit(EXIT_FAILURE);
 58    }
 59    printf("TCP Echo Server listening on port %d (max %d clients)\n",
 60           port, MAX_CLIENTS);
 61
 62    /* Setup select */
 63    int client_fds[MAX_CLIENTS];
 64    for (int i = 0; i < MAX_CLIENTS; i++)
 65        client_fds[i] = -1;
 66
 67    fd_set active_fds, read_fds;
 68    FD_ZERO(&active_fds);
 69    FD_SET(server_fd, &active_fds);
 70    int max_fd = server_fd;
 71
 72    char buffer[BUF_SIZE];
 73
 74    /* Main loop */
 75    while (1) {
 76        read_fds = active_fds;
 77        int ready = select(max_fd + 1, &read_fds, NULL, NULL, NULL);
 78        if (ready < 0) {
 79            if (errno == EINTR) continue;
 80            perror("select");
 81            break;
 82        }
 83
 84        /* New connection? */
 85        if (FD_ISSET(server_fd, &read_fds)) {
 86            struct sockaddr_in client_addr;
 87            socklen_t len = sizeof(client_addr);
 88            int new_fd = accept(server_fd,
 89                               (struct sockaddr *)&client_addr, &len);
 90            if (new_fd >= 0) {
 91                int added = 0;
 92                for (int i = 0; i < MAX_CLIENTS; i++) {
 93                    if (client_fds[i] == -1) {
 94                        client_fds[i] = new_fd;
 95                        FD_SET(new_fd, &active_fds);
 96                        if (new_fd > max_fd) max_fd = new_fd;
 97
 98                        char ip[INET_ADDRSTRLEN];
 99                        inet_ntop(AF_INET, &client_addr.sin_addr,
100                                  ip, sizeof(ip));
101                        printf("[+] Client connected: %s:%d (fd=%d)\n",
102                               ip, ntohs(client_addr.sin_port), new_fd);
103                        added = 1;
104                        break;
105                    }
106                }
107                if (!added) {
108                    const char *msg = "Server full\n";
109                    send(new_fd, msg, strlen(msg), 0);
110                    close(new_fd);
111                }
112            }
113        }
114
115        /* Check client sockets */
116        for (int i = 0; i < MAX_CLIENTS; i++) {
117            int fd = client_fds[i];
118            if (fd == -1) continue;
119
120            if (FD_ISSET(fd, &read_fds)) {
121                ssize_t bytes = recv(fd, buffer, BUF_SIZE - 1, 0);
122                if (bytes <= 0) {
123                    printf("[-] Client disconnected (fd=%d)\n", fd);
124                    close(fd);
125                    FD_CLR(fd, &active_fds);
126                    client_fds[i] = -1;
127                } else {
128                    buffer[bytes] = '\0';
129                    printf("[fd=%d] %s", fd, buffer);
130                    send(fd, buffer, bytes, 0);
131                }
132            }
133        }
134    }
135
136    close(server_fd);
137    return 0;
138}