試験ノート - 実装ドリル 1.0
読み取り中…
検索中…
一致する文字列を見つけられません
mini_serv.c
1
177#include <unistd.h>
178#include <stdlib.h>
179#include <string.h>
180#include <stdio.h>
181#include <sys/socket.h>
182#include <sys/select.h>
183#include <netinet/in.h>
184
185// グローバル状態 — 役割は @defgroup mini_serv の "グローバル状態" 表を参照。
186// 非自明な 3 つ (g_ids / g_msgs / g_buf_write) のみ @var で詳解。
187static int g_listen_fd;
188static int g_maxfd;
189static int g_next_id;
190
208static int g_ids[65536];
209
230static char *g_msgs[65536];
231
232static fd_set g_readfds;
233static fd_set g_writefds;
234static fd_set g_activefds;
235static char g_buf_read[4097];
236
253static char g_buf_write[200042];
254
258int main(int ac, char **av);
259static void fatal(void);
260static void accept_client(void);
261static void receive_from(int fd);
262static void send_all(int except);
263static void disconnect_client(int fd);
264static char *str_join(char *buf, char *add);
265static int extract_message(char **buf, char **msg);
266
299int main(int ac, char **av)
300{
301 if (ac != 2)
302 {
303 write(2, "Wrong number of arguments\n", 26);
304 exit(1);
305 }
306
307 g_listen_fd = socket(AF_INET, SOCK_STREAM, 0);
308 if (g_listen_fd < 0)
309 fatal();
310
311 struct sockaddr_in addr;
312 bzero(&addr, sizeof(addr));
313 addr.sin_family = AF_INET;
314 addr.sin_addr.s_addr = htonl(0x7f000001);
315 addr.sin_port = htons(atoi(av[1]));
316 if (bind(g_listen_fd, (const struct sockaddr *)&addr, sizeof(addr)) < 0)
317 fatal();
318 if (listen(g_listen_fd, 128) < 0)
319 fatal();
320
321 FD_ZERO(&g_activefds);
322 FD_SET(g_listen_fd, &g_activefds);
323 g_maxfd = g_listen_fd;
324 g_next_id = 0;
325 bzero(g_ids, sizeof(g_ids));
326 bzero(g_msgs, sizeof(g_msgs));
327
328 while (1)
329 {
330 g_readfds = g_writefds = g_activefds;
331 if (select(g_maxfd + 1, &g_readfds, &g_writefds, NULL, NULL) < 0)
332 continue;
333
334 for (int fd = 0; fd <= g_maxfd; fd++)
335 {
336 if (!FD_ISSET(fd, &g_readfds))
337 continue;
338 if (fd == g_listen_fd)
340 else
341 receive_from(fd);
342 break;
343 }
344 }
345 return 0;
346}
347
358static void fatal(void)
359{
360 write(2, "Fatal error\n", 12);
361 exit(1);
362}
363
379static void accept_client(void)
380{
381 struct sockaddr_in cli;
382 socklen_t clen = sizeof(cli);
383 int cfd = accept(g_listen_fd, (struct sockaddr *)&cli, &clen);
384 if (cfd < 0)
385 fatal();
386 if (cfd > g_maxfd)
387 g_maxfd = cfd;
388 g_ids[cfd] = g_next_id++;
389 g_msgs[cfd] = NULL;
390 FD_SET(cfd, &g_activefds);
391 sprintf(g_buf_write, "server: client %d just arrived\n", g_ids[cfd]);
392 send_all(cfd);
393}
394
415static void receive_from(int fd)
416{
417 int r = recv(fd, g_buf_read, 4096, 0);
418 if (r <= 0)
419 {
421 return;
422 }
423 g_buf_read[r] = '\0';
424 g_msgs[fd] = str_join(g_msgs[fd], g_buf_read);
425
426 char *msg;
427 while (extract_message(&g_msgs[fd], &msg))
428 {
429 sprintf(g_buf_write, "client %d: %s", g_ids[fd], msg);
430 send_all(fd);
431 free(msg);
432 }
433}
434
451static void send_all(int except)
452{
453 int len = strlen(g_buf_write);
454 for (int fd = 0; fd <= g_maxfd; fd++)
455 {
456 if (fd == g_listen_fd || fd == except)
457 continue;
458 if (FD_ISSET(fd, &g_writefds))
459 send(fd, g_buf_write, len, 0);
460 }
461}
462
478static void disconnect_client(int fd)
479{
480 sprintf(g_buf_write, "server: client %d just left\n", g_ids[fd]);
481 FD_CLR(fd, &g_activefds);
482 close(fd);
483 send_all(fd);
484 free(g_msgs[fd]);
485 g_msgs[fd] = NULL;
486}
487
507static char *str_join(char *buf, char *add)
508{
509 int len = (buf ? (int)strlen(buf) : 0) + (int)strlen(add) + 1;
510 char *res = malloc(len);
511 if (!res)
512 fatal();
513 res[0] = '\0';
514 if (buf)
515 strcat(res, buf);
516 free(buf);
517 strcat(res, add);
518 return res;
519}
520
539static int extract_message(char **buf, char **msg)
540{
541 *msg = NULL;
542 if (!*buf)
543 return 0;
544 for (int i = 0; (*buf)[i]; i++)
545 {
546 if ((*buf)[i] == '\n')
547 {
548 char *newbuf = calloc(strlen(*buf + i + 1) + 1, sizeof(char));
549 if (!newbuf)
550 fatal();
551 strcpy(newbuf, *buf + i + 1);
552 *msg = *buf;
553 (*msg)[i + 1] = '\0';
554 *buf = newbuf;
555 return 1;
556 }
557 }
558 return 0;
559}
560
// mini_serv 終わり
int main(int ac, char **av)
プログラムのエントリポイント
Definition mini_serv.c:299
static void accept_client(void)
新規クライアントを accept し、ID を採番して入室通知をブロードキャスト
Definition mini_serv.c:379
static int extract_message(char **buf, char **msg)
蓄積バッファから先頭の 1 行を切り出す
Definition mini_serv.c:539
static void fatal(void)
Fatal error\n を stderr に書いて exit(1) する
Definition mini_serv.c:358
static void receive_from(int fd)
1 つのクライアント fd からデータを読み、行ごとに整形・転送する
Definition mini_serv.c:415
static void disconnect_client(int fd)
クライアントを切断し、退室通知をブロードキャスト + 後始末
Definition mini_serv.c:478
static char * str_join(char *buf, char *add)
既存バッファ buf に add を結合した新しいバッファを返す (buf は free)
Definition mini_serv.c:507
static void send_all(int except)
g_buf_write の内容を except と listen 以外の全クライアントへ送信する
Definition mini_serv.c:451