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}