Document of c-modernization-kit (porter) 1.0.0
Loading...
Searching...
No Matches
tcpServer_linux.c
Go to the documentation of this file.
1
22
23#ifndef _WIN32
24
25#include <stdio.h>
26#include <stdlib.h>
27#include <string.h>
28#include <sys/socket.h>
29#include <sys/epoll.h>
30#include <sys/wait.h>
31#include <netinet/in.h>
32#include <signal.h>
33#include <errno.h>
34
36#define MAX_EPOLL_EVENTS 64
37
38#include "tcpServer.h"
39
40static volatile sig_atomic_t running = 1;
41
42/* ============================================================
43 * シグナルハンドラ
44 * ============================================================ */
45
54static void sigchld_handler(int sig) {
55 (void)sig;
56 while (waitpid(-1, NULL, WNOHANG) > 0);
57}
58
67static void shutdown_handler(int sig) {
68 (void)sig;
69 running = 0;
70}
71
72/* ============================================================
73 * 内部ヘルパー
74 * ============================================================ */
75
85static int create_listen_socket(int port) {
86 int server_fd;
87 struct sockaddr_in addr;
88 int opt = 1;
89
90 server_fd = socket(AF_INET, SOCK_STREAM, 0);
91 if (server_fd < 0) {
92 perror("socket");
93 exit(1);
94 }
95
96 setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
97
98 memset(&addr, 0, sizeof(addr));
99 addr.sin_family = AF_INET;
100 addr.sin_addr.s_addr = INADDR_ANY;
101 addr.sin_port = htons((uint16_t)port);
102
103 if (bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
104 perror("bind");
105 exit(1);
106 }
107
108 if (listen(server_fd, 128) < 0) {
109 perror("listen");
110 exit(1);
111 }
112
113 return server_fd;
114}
115
132static void worker_loop(int server_fd, int worker_id, int conns_per_worker) {
133 printf("[ワーカー %d, PID %lu] 起動完了、接続待機\n", worker_id, (unsigned long)get_pid());
134
135 if (conns_per_worker == 1) {
136 /* --- 従来の逐次処理 --- */
137 struct sockaddr_in client_addr;
138 socklen_t addr_len;
139 int client_fd;
140
141 while (running) {
142 addr_len = sizeof(client_addr);
143 client_fd = accept(server_fd, (struct sockaddr *)&client_addr, &addr_len);
144
145 if (client_fd < 0) {
146 if (errno == EINTR) {
147 continue;
148 }
149 perror("accept");
150 continue;
151 }
152
153 printf("[ワーカー %d] クライアント接続\n", worker_id);
154 g_session_fn(client_fd);
155 printf("[ワーカー %d] 次の接続待機\n", worker_id);
156 }
157 } else {
158 /* --- epoll による多重接続処理 --- */
159 struct epoll_event ev;
160 struct epoll_event events[MAX_EPOLL_EVENTS];
161 char buf[BUFFER_SIZE];
162 int active_count = 0;
163 int accepting = 1; /* server_fd が epoll に登録済みか */
164
165 int epoll_fd = epoll_create1(0);
166 if (epoll_fd < 0) {
167 perror("epoll_create1");
168 exit(1);
169 }
170
171 ev.events = EPOLLIN;
172 ev.data.fd = server_fd;
173 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) < 0) {
174 perror("epoll_ctl add server_fd");
175 exit(1);
176 }
177
178 while (running) {
179 int nfds = epoll_wait(epoll_fd, events, MAX_EPOLL_EVENTS, -1);
180 if (nfds < 0) {
181 if (errno == EINTR) {
182 continue;
183 }
184 perror("epoll_wait");
185 break;
186 }
187
188 for (int i = 0; i < nfds; i++) {
189 int fd = events[i].data.fd;
190
191 if (fd == server_fd) {
192 /* 新規接続 */
193 int client_fd = accept(server_fd, NULL, NULL);
194 if (client_fd < 0) {
195 if (errno != EINTR) {
196 perror("accept");
197 }
198 continue;
199 }
200
201 ev.events = EPOLLIN;
202 ev.data.fd = client_fd;
203 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, client_fd, &ev) < 0) {
204 perror("epoll_ctl add client_fd");
205 close(client_fd);
206 continue;
207 }
208
209 active_count++;
210 printf("[ワーカー %d] 新規接続 (計 %d 接続)\n", worker_id, active_count);
211
212 /* 容量到達 → server_fd を epoll から除去して新規 accept を止める */
213 if (active_count >= conns_per_worker) {
214 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, server_fd, NULL);
215 accepting = 0;
216 }
217
218 } else {
219 /* 既存接続のデータ到着または切断 */
220 ssize_t n = client_recv(fd, buf, sizeof(buf));
221 if (n <= 0) {
222 epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL);
223 client_close(fd);
224 active_count--;
225 printf("[ワーカー %d] 接続終了 (残 %d 接続)\n", worker_id, active_count);
226
227 /* 空きが生じた → server_fd を再登録して accept を再開 */
228 if (!accepting && active_count < conns_per_worker) {
229 ev.events = EPOLLIN;
230 ev.data.fd = server_fd;
231 if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, server_fd, &ev) == 0) {
232 accepting = 1;
233 }
234 }
235 } else {
236 client_send(fd, buf, (size_t)n);
237 }
238 }
239 }
240 }
241
242 close(epoll_fd);
243 }
244
245 printf("[ワーカー %d] 終了\n", worker_id);
246 exit(0);
247}
248
249/* ============================================================
250 * プラットフォームフック
251 * ============================================================ */
252
253/* doxygen コメントは、ヘッダに記載 */
255 g_session_fn = session_fn;
256}
257
258/* doxygen コメントは、ヘッダに記載 */
260 /* Linux では後処理不要 */
261}
262
263/* doxygen コメントは、ヘッダに記載 */
264int dispatch_internal_args(int argc, char *argv[]) {
265 (void)argc;
266 (void)argv;
267 return 0;
268}
269
270/* ============================================================
271 * サーバー実装
272 * ============================================================ */
273
274/* doxygen コメントは、ヘッダに記載 */
275void run_fork_server(int port) {
276 int server_fd, client_fd;
277 struct sockaddr_in addr;
278 socklen_t addr_len = sizeof(addr);
279 struct sigaction sa;
280
281 /* SIGCHLD ハンドラ設定 (ゾンビプロセス回避) */
282 sa.sa_handler = sigchld_handler;
283 sigemptyset(&sa.sa_mask);
284 sa.sa_flags = SA_RESTART;
285 sigaction(SIGCHLD, &sa, NULL);
286
287 server_fd = create_listen_socket(port);
288 printf("[親プロセス %lu] fork モード、ポート %d で待ち受け開始\n", (unsigned long)get_pid(), port);
289
290 while (1) {
291 client_fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len);
292 if (client_fd < 0) {
293 perror("accept");
294 continue;
295 }
296
297 printf("[親プロセス] 接続受付、子プロセス生成\n");
298
299 pid_t pid = fork();
300 if (pid < 0) {
301 perror("fork");
302 close(client_fd);
303 } else if (pid == 0) {
304 /* 子プロセス */
305 close(server_fd);
306 g_session_fn(client_fd);
307 exit(0);
308 } else {
309 /* 親プロセス */
310 close(client_fd);
311 }
312 }
313}
314
315/* doxygen コメントは、ヘッダに記載 */
316void run_prefork_server(int port, int num_workers, int conns_per_worker) {
317 int server_fd;
318 struct sigaction sa;
319
320 /* SIGINT/SIGTERM ハンドラ設定 */
321 sa.sa_handler = shutdown_handler;
322 sigemptyset(&sa.sa_mask);
323 sa.sa_flags = 0;
324 sigaction(SIGINT, &sa, NULL);
325 sigaction(SIGTERM, &sa, NULL);
326
327 /* SIGCHLD ハンドラ設定 */
328 sa.sa_handler = sigchld_handler;
329 sa.sa_flags = SA_RESTART;
330 sigaction(SIGCHLD, &sa, NULL);
331
332 server_fd = create_listen_socket(port);
333 printf("[親プロセス %lu] prefork モード、ポート %d で待ち受け開始\n", (unsigned long)get_pid(), port);
334 printf("[親プロセス] %d 個のワーカープロセスを起動 (1 ワーカーあたり最大 %d 接続)\n",
335 num_workers, conns_per_worker);
336
337 pid_t *worker_pids = malloc((size_t)num_workers * sizeof(pid_t));
338 if (!worker_pids) {
339 perror("malloc");
340 exit(1);
341 }
342
343 for (int i = 0; i < num_workers; i++) {
344 pid_t pid = fork();
345 if (pid < 0) {
346 perror("fork");
347 free(worker_pids);
348 exit(1);
349 } else if (pid == 0) {
350 free(worker_pids);
351 worker_loop(server_fd, i, conns_per_worker);
352 /* ここには到達しない */
353 }
354 worker_pids[i] = pid;
355 }
356
357 printf("[親プロセス] Ctrl+C で終了\n");
358
359 while (running) {
360 pause();
361 }
362
363 printf("\n[親プロセス] 全ワーカーを終了させます\n");
364 for (int i = 0; i < num_workers; i++) {
365 if (worker_pids[i] > 0) {
366 kill(worker_pids[i], SIGTERM);
367 }
368 }
369
370 for (int i = 0; i < num_workers; i++) {
371 if (worker_pids[i] > 0) {
372 waitpid(worker_pids[i], NULL, 0);
373 }
374 }
375
376 free(worker_pids);
377 close(server_fd);
378 printf("[親プロセス] 終了\n");
379}
380
381#else /* _WIN32 */
382 #pragma warning(disable : 4206)
383#endif /* _WIN32 */
TCP サーバーサンプル共通定義。
void(* ClientSessionFn)(ClientFd fd)
セッション処理関数の型。
Definition tcpServer.h:99
#define BUFFER_SIZE
送受信バッファサイズ (バイト)。
Definition tcpServer.h:76
#define client_close(fd)
クライアントソケットを閉じる。
Definition tcpServer.h:40
ClientSessionFn g_session_fn
登録済みセッション処理関数。
#define client_recv(fd, buf, len)
クライアントからデータを受信する。
Definition tcpServer.h:36
#define get_pid()
現在のプロセス ID を取得する。
Definition tcpServer.h:34
#define client_send(fd, buf, len)
クライアントへデータを送信する。
Definition tcpServer.h:38
void platform_init(ClientSessionFn session_fn)
プラットフォーム初期化 (Windows: WSAStartup / Linux: no-op)。
static void sigchld_handler(int sig)
SIGCHLD シグナルハンドラ。
int dispatch_internal_args(int argc, char *argv[])
内部起動引数を処理します。
void platform_cleanup(void)
プラットフォーム後処理 (Windows: WSACleanup / Linux: no-op)。
void run_prefork_server(int port, int num_workers, int conns_per_worker)
prefork モードのサーバーを起動します。
static void worker_loop(int server_fd, int worker_id, int conns_per_worker)
ワーカープロセスのメインループ (prefork モード用)。
void run_fork_server(int port)
fork モードのサーバーを起動します。
static void shutdown_handler(int sig)
SIGINT / SIGTERM シグナルハンドラ。
static volatile sig_atomic_t running
static int create_listen_socket(int port)
listen ソケットを作成してバインドし、待ち受けを開始します。
#define MAX_EPOLL_EVENTS
epoll_wait で一度に取得する最大イベント数。