uni

University stuff
git clone git://git.margiolis.net/uni.git
Log | Files | Refs | README | LICENSE

commit 7e43536e1f58aff2a918d9b8c435dd1ae4995327
parent a93b9c0ff9b184453a95cdde7edbe3700c5a4613
Author: Christos Margiolis <christos@FreeBSD.org>
Date:   Tue, 11 Jun 2024 01:33:05 +0200

bar

Diffstat:
Mconcurrent_programming/Makefile | 2+-
Mconcurrent_programming/extern.h | 39++++++++++++++++++++++++++++++++++++++-
Mconcurrent_programming/rps_client.c | 99++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Mconcurrent_programming/rps_server.c | 311++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
4 files changed, 290 insertions(+), 161 deletions(-)

diff --git a/concurrent_programming/Makefile b/concurrent_programming/Makefile @@ -1,6 +1,6 @@ all: cc rps_server.c -o rps_server - cc rps_client.c -lncurses -o rps_client + cc rps_client.c -o rps_client clean: rm -f rps_server rps_client *.core *.o diff --git a/concurrent_programming/extern.h b/concurrent_programming/extern.h @@ -3,7 +3,9 @@ #define CMD_LEN 32 #define NAME_LEN 32 -#define MOVE_TIMEOUT 3 +#define CLIENTS_MAX 64 +#define MOVE_TIMEOUT 10 +#define BUF_LEN (BUFSIZ << 2) #define ARRLEN(x) (sizeof(x) / sizeof(x[0])) enum { @@ -12,4 +14,39 @@ enum { MOVE_SCISSOR, }; +static inline int str2move(const char *); +static inline const char *move2str(int); + +static inline int +str2move(const char *str) +{ + int move; + + if (strcmp(str, "rock") == 0) + move = MOVE_ROCK; + else if (strcmp(str, "paper") == 0) + move = MOVE_PAPER; + else if (strcmp(str, "scissor") == 0) + move = MOVE_SCISSOR; + else + move = -1; + + return (move); +} + +static inline const char * +move2str(int move) +{ + switch (move) { + case MOVE_ROCK: + return ("rock"); + case MOVE_PAPER: + return ("paper"); + case MOVE_SCISSOR: + return ("scissor"); + default: + return ("unknown"); + } +} + #endif /* _EXTERN_H_ */ diff --git a/concurrent_programming/rps_client.c b/concurrent_programming/rps_client.c @@ -16,13 +16,15 @@ #include "extern.h" +#define NTDS 2 + struct command { char name[CMD_LEN]; int (*func)(char *); }; static int cmd_challenge(char *); -static int cmd_play(char *); +static int cmd_accept(char *); static int cmd_msg(char *); static int cmd_list(char *); static int cmd_quit(char *); @@ -32,27 +34,42 @@ static void sighandler(int); static void usage(void); static struct command commands[] = { - { "challenge", cmd_challenge }, /* /challenge <id> */ - { "play", cmd_play }, /* /play <rock|paper|scissor> */ - { "msg", cmd_msg }, /* /msg <message> */ - { "list", cmd_list }, /* /list */ - { "quit", cmd_quit }, /* /quit */ - { "help", cmd_help }, /* /help */ + { "challenge", cmd_challenge },/* /challenge <id> <rock|paper|scissor> */ + { "accept", cmd_accept }, /* /accept <rock|paper|scissor> */ + { "msg", cmd_msg }, /* /msg <message> */ + { "list", cmd_list }, /* /list */ + { "quit", cmd_quit }, /* /quit */ + { "help", cmd_help }, /* /help */ }; static int ymax; static int xmax; -static int f_quit = 0; static int fd; +static volatile sig_atomic_t f_quit = 0; static int cmd_challenge(char *args) { - char cmd[CMD_LEN]; - int id; + char cmd[CMD_LEN], *s; + int id, move; if (args == NULL) { - warnx("usage: /challenge <id>"); + warnx("usage: /challenge <id> <rock|paper|scissor>"); + return (-1); + } + s = strchr(args, ' '); + if (s == NULL || *s == '\0') { + warnx("usage: /challenge <id> <rock|paper|scissor>"); + return (-1); + } + while (*s == ' ') + s++; + if (*s == '\0') { + warnx("usage: /challenge <id> <rock|paper|scissor>"); + return (-1); + } + if ((move = str2move(s)) < 0) { + warnx("invalid move: %s", s); return (-1); } @@ -72,11 +89,16 @@ cmd_challenge(char *args) return (-1); } + if (send(fd, &move, sizeof(move), 0) < 0) { + warn("send(challenge, move)"); + return (-1); + } + return (0); } static int -cmd_play(char *args) +cmd_accept(char *args) { char cmd[CMD_LEN]; int move; @@ -86,23 +108,19 @@ cmd_play(char *args) return (-1); } - strlcpy(cmd, "play", sizeof(cmd)); + strlcpy(cmd, "accept", sizeof(cmd)); if (send(fd, cmd, sizeof(cmd), 0) < 0) { - warn("send(play, cmd)"); + warn("send(accept, cmd)"); return (-1); } - if (strcmp(args, "rock") == 0) - move = MOVE_ROCK; - else if (strcmp(args, "paper") == 0) - move = MOVE_PAPER; - else if (strcmp(args, "scissor") == 0) - move = MOVE_SCISSOR; - else - warnx("invalid move: %s\n", args); + if ((move = str2move(args)) < 0) { + warnx("invalid move: %s", args); + return (-1); + } if (send(fd, &move, sizeof(move), 0) < 0) { - warn("send(play, move)"); + warn("send(accept, move)"); return (-1); } @@ -113,6 +131,7 @@ static int cmd_msg(char *args) { char cmd[CMD_LEN]; + size_t len; if (args == NULL) { warnx("usage: /msg <message>"); @@ -125,13 +144,24 @@ cmd_msg(char *args) return (-1); } + len = strlen(args) + 1; + if (send(fd, &len, sizeof(len), 0) < 0) { + warn("send(msg, len)"); + return (-1); + } + + if (send(fd, args, len, 0) < 0) { + warn("send(msg, msg)"); + return (-1); + } + return (0); } static int cmd_list(char *args) { - char cmd[CMD_LEN]; + char cmd[CMD_LEN], buf[BUF_LEN]; strlcpy(cmd, "list", sizeof(cmd)); if (send(fd, cmd, sizeof(cmd), 0) < 0) { @@ -139,6 +169,12 @@ cmd_list(char *args) return (-1); } + if (recv(fd, buf, sizeof(buf), 0) < 0) { + warn("recv(list)"); + return (-1); + } + puts(buf); + return (0); } @@ -162,8 +198,9 @@ static int cmd_help(char *args) { printf("Available commands:\n"); - printf("/challenge <id|name>\t\tChallenge a player\n"); - printf("/play <rock|paper|scisssor>\tMake a move\n"); + printf("/challenge <id|name> <rock|paper|scissor>\t\t" + "Challenge a player\n"); + printf("/accept <rock|paper|scisssor>\tAccept challenge\n"); printf("/msg <message>\t\t\tSend a message to the global chat\n"); printf("/quit\t\t\t\tQuit the game\n"); printf("/help\t\t\t\tShow this help message\n"); @@ -174,7 +211,7 @@ cmd_help(char *args) static struct command * parse_command(char **args) { - char buf[BUFSIZ], *line, *cmd, *s; + char buf[BUF_LEN], *line, *cmd, *s; int i; printf("\r> "); @@ -255,7 +292,8 @@ main(int argc, char *argv[]) struct hostent *hp; struct sigaction sa; struct command *cmd; - char *args, *s; + char *args; + char *s; char nick[NAME_LEN]; int port = 9999; int ch; @@ -311,6 +349,7 @@ main(int argc, char *argv[]) break; } + /* Set up connection. */ if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) err(1, "socket(AF_INET)"); memset(&sin, 0, sizeof(sin)); @@ -328,12 +367,13 @@ main(int argc, char *argv[]) if (send(fd, nick, sizeof(nick), 0) < 0) err(1, "send(nick)"); + /* Print list of available players. */ cmd_list(NULL); + /* Main game loop. */ for (;;) { if (f_quit) break; - /* TODO redraw */ if ((cmd = parse_command(&args)) == NULL) continue; if (cmd->func(args) < 0) @@ -341,6 +381,7 @@ main(int argc, char *argv[]) if (args != NULL) free(args); } + close(fd); return (0); diff --git a/concurrent_programming/rps_server.c b/concurrent_programming/rps_server.c @@ -1,5 +1,4 @@ #include <sys/mman.h> -#include <sys/queue.h> #include <sys/socket.h> #include <sys/types.h> #include <sys/wait.h> @@ -17,147 +16,216 @@ #include "extern.h" -struct user { +struct client { int fd; int id; char name[NAME_LEN]; int move; int nplays; int nwins; - struct user *opponent; - TAILQ_ENTRY(user) next; + struct client *opponent; }; -static int srv(struct user *); +static void remove_client(struct client *); +static int srv(int); static void sighandler(int); static void usage(void); +static int *ids; +static int *nclients; +static struct client *clients; static volatile sig_atomic_t f_quit = 0; -static int ids = 0; -static int *nusers; -static TAILQ_HEAD(, user) users = TAILQ_HEAD_INITIALIZER(users); static void -remove_user(struct user *user) +remove_client(struct client *c) { - struct user *up; - - while (!TAILQ_EMPTY(&users)) { - up = TAILQ_FIRST(&users); - TAILQ_REMOVE(&users, up, next); - close(up->fd); - munmap(up, sizeof(struct user)); - break; + struct client *cp; + int i; + + if (c->id < 1) + return; + for (i = 0; i < CLIENTS_MAX; i++) { + cp = &clients[i]; + if (cp == c) { + printf("%s: client disconnected: %s#%d (fd=%d)\n", + getprogname(), cp->name, cp->id, cp->fd); + close(cp->fd); + memset(cp, 0, sizeof(*cp)); + (*nclients)--; + break; + } } } static int -srv(struct user *user) +srv(int fd) { - struct user *up; - char cmd[CMD_LEN]; - int rc = 0, tmp, quit, id; + struct client *c, *cp; + char cmd[CMD_LEN], buf[BUF_LEN]; + size_t len; + int rc = 0, i, tmp, quit, id; + + c = &clients[*nclients]; + memset(c, 0, sizeof(*c)); + c->fd = fd; + c->id = ++(*ids); + c->move = -1; + + (*nclients)++; + + if (recv(c->fd, c->name, sizeof(c->name), 0) < 0) { + warn("recv(%d, nick)", c->fd); + remove_client(c); + return (-1); + } + + printf("%s: active clients=%d\n", getprogname(), *nclients); + printf("%s: client connected: %s#%d (fd=%d)\n", + getprogname(), c->name, c->id, c->fd); for (;;) { - tmp = recv(user->fd, cmd, sizeof(cmd), 0); + memset(buf, 0, sizeof(buf)); + tmp = recv(c->fd, cmd, sizeof(cmd), 0); if (tmp < 0) { - warn("recv(%d, cmd)", user->fd); + warn("recv(%d, cmd)", c->fd); rc = -1; break; } else if (tmp == 0) break; if (strcmp(cmd, "challenge") == 0) { - if (recv(user->fd, &id, sizeof(id), 0) < 0) { - warn("recv(%d, id)", user->fd); + if (recv(c->fd, &id, sizeof(id), 0) < 0) { + warn("recv(%d, challenge, id)", c->fd); + rc = -1; + break; + } + if (recv(c->fd, &c->move, sizeof(c->move), 0) < 0) { + warn("recv(%d, challenge, move)", c->fd); rc = -1; break; } - up = NULL; - TAILQ_FOREACH(up, &users, next) { - if (up->id == id) + if (c->id == id) { + warnx("%s tried to challenge themselves", + c->name); + continue; + } + cp = NULL; + for (i = 0; i < CLIENTS_MAX; i++) { + cp = &clients[i]; + if (cp->id > 0 && cp->id == id) break; + cp = NULL; } - if (up == NULL) { + if (cp == NULL) { warnx("id=%d not found", id); continue; } - if (up->opponent != NULL) { - warnx("%s is already challenged", up->name); + if (cp->opponent != NULL) { + warnx("%s is already challenged", cp->name); continue; } - user->opponent = up; - up->opponent = user; - printf("%s is challenging %d\n", user->name, id); - /* TODO challenge available user */ - } else if (strcmp(cmd, "play") == 0) { - if (recv(user->fd, &user->move, - sizeof(user->move), 0) < 0) { - warn("recv(%d, play)", user->fd); + c->opponent = cp; + cp->opponent = c; + + printf("%s is challenging %s with %s\n", + c->name, cp->name, move2str(c->move)); + for (i = 0; i < MOVE_TIMEOUT; i++) { + if (c->opponent != NULL) + sleep(1); + else + break; + } + printf("%s woke up\n", c->name); + if (c->opponent != NULL) { + printf("%s didn't make a move\n", c->opponent->name); + c->nwins++; + c->move = -1; + c->nplays++; + c->opponent->move = -1; + c->opponent->nplays++; + c->opponent->opponent = NULL; + c->opponent = NULL; + } + } else if (strcmp(cmd, "accept") == 0) { + if (recv(c->fd, &c->move, sizeof(c->move), 0) < 0) { + warn("recv(%d, accept)", c->fd); rc = -1; break; } - /* Cannot play when unchallenged. */ - if (user->opponent == NULL) + if (c->opponent == NULL) { + warnx("%s: cannot play when unchallenged", + c->name); continue; - - /* Give the opponent 3 seconds to make a move. */ - for (int i = 0; user->opponent->move < 0 && - i < MOVE_TIMEOUT; i++) { - printf("[%s - %s] %s sleeping\n", - user->name, user->opponent->name, user->name); - sleep(1); - } - - /* Didn't respond, he lost. */ - if (user->opponent->move < 0) { - printf("[%s - %s] opponent didn't respond\n", - user->name, user->opponent->name); - user->move = -1; - user->opponent = NULL; - /*user->opponent->opponent = NULL;*/ - user->nplays++; - user->nwins++; } - printf("%s=%d, %s=%d\n", user->name, user->move, - user->opponent->name, user->opponent->move); - - switch (user->move) { + switch (c->move) { case MOVE_ROCK: - if (user->opponent->move == MOVE_SCISSOR) - user->nwins++; + if (c->opponent->move == MOVE_SCISSOR) + c->nwins++; + else if (c->opponent->move == MOVE_PAPER) + c->opponent->nwins++; break; case MOVE_PAPER: - if (user->opponent->move == MOVE_ROCK) - user->nwins++; + if (c->opponent->move == MOVE_ROCK) + c->nwins++; + else if (c->opponent->move == MOVE_SCISSOR) + c->opponent->nwins++; break; case MOVE_SCISSOR: - if (user->opponent->move == MOVE_PAPER) - user->nwins++; + if (c->opponent->move == MOVE_PAPER) + c->nwins++; + else if (c->opponent->move == MOVE_ROCK) + c->opponent->nwins++; break; default: - warnx("invalid move: %d", user->move); + warnx("invalid move: %s", move2str(c->move)); continue; } - printf("[%s - %s] = [%d - %d]\n", - user->name, user->opponent->name, - user->nwins, user->opponent->nwins); - - user->move = -1; - user->opponent = NULL; - user->opponent->opponent = NULL; - user->nplays++; + printf("%s (%d) - %s (%d)\n", + c->name, c->nwins, + c->opponent->name, c->opponent->nwins); + + c->move = -1; + c->nplays++; + c->opponent->move = -1; + c->opponent->nplays++; + c->opponent->opponent = NULL; + c->opponent = NULL; } else if (strcmp(cmd, "msg") == 0) { - /* TODO post message to global chat (list) */ + if (recv(c->fd, &len, sizeof(len), 0) < 0) { + warn("recv(%d, msg, len)", c->fd); + rc = -1; + break; + } + if (len < 0 || len > BUF_LEN) { + warnx("invalid length: %ld", len); + continue; + } + if (recv(c->fd, buf, len, 0) < 0) { + warn("recv(%d, msg, msg)", c->fd); + rc = -1; + break; + } + buf[len] = '\0'; + printf("<%s> %s\n", c->name, buf); + /* TODO send message to client */ } else if (strcmp(cmd, "list") == 0) { - printf("ID\tNAME\tPLAYED\tWON\n"); - TAILQ_FOREACH(up, &users, next) { - printf("%d\t%s\t%d\t%d\n", - up->id, up->name, up->nplays, up->nwins); + snprintf(buf, sizeof(buf), + "ID\tNAME\tPLAYED\tWON\n"); + for (i = 0; i < CLIENTS_MAX; i++) { + cp = &clients[i]; + if (cp->id == 0) + continue; + snprintf(buf + strlen(buf), sizeof(buf), + "%d\t%s\t%d\t%d\n", + cp->id, cp->name, cp->nplays, cp->nwins); + } + if (send(c->fd, buf, sizeof(buf), 0) < 0) { + warn("send(%d, list)", c->fd); + rc = -1; + break; } - /* TODO send as nvlist */ } else if (strcmp(cmd, "quit") == 0) { break; } else { @@ -165,9 +233,7 @@ srv(struct user *user) } } - printf("%s: client disconnected: %s#%d (fd=%d)\n", - getprogname(), user->name, user->id, user->fd); - remove_user(user); + remove_client(c); return (rc); } @@ -192,11 +258,10 @@ main(int argc, char *argv[]) struct sockaddr_in sin; struct hostent *hp; struct sigaction sa; - struct user *user; int backlog = 10; int port = 9999; - int sfd; - int ch; + int sfd, cfd; + int ch, i; while ((ch = getopt(argc, argv, "b:p:")) != -1) { switch (ch) { @@ -273,52 +338,35 @@ main(int argc, char *argv[]) if (listen(sfd, backlog) < 0) err(1, "listen"); - /* mmap(2) this that we can modify decrement it from the child. */ - nusers = mmap(NULL, sizeof(*nusers), PROT_READ | PROT_WRITE, + /* Use mmap(2) that we can modify them from the child. */ + nclients = mmap(NULL, sizeof(*nclients), PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if (nusers == MAP_FAILED) + if (nclients == MAP_FAILED) + err(1, "mmap"); + *nclients = 0; + + clients = mmap(NULL, sizeof(struct client) * CLIENTS_MAX, + PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (clients == MAP_FAILED) err(1, "mmap"); - *nusers = 0; + memset(clients, 0, sizeof(struct client) * CLIENTS_MAX); - TAILQ_INIT(&users); + ids = mmap(NULL, sizeof(*ids), PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_ANONYMOUS, -1, 0); + if (ids == MAP_FAILED) + err(1, "mmap"); + *ids = 0; for (;;) { /* We caught a termination signal. */ if (f_quit) break; - - /* - * Allocate user structure using mmap(2) so that we can modify - * the userlist from the child process. - */ - user = mmap(NULL, sizeof(struct user), PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_ANONYMOUS, -1, 0); - if (user == MAP_FAILED) - err(1, "mmap"); - - if ((user->fd = accept(sfd, NULL, NULL)) < 0) { - warn("accept(%d)", user->fd); - remove_user(user); + if ((*nclients + 1) > CLIENTS_MAX) continue; - } - /* Receive nickname and assign ID to user. */ - if (recv(user->fd, user->name, sizeof(user->name), 0) < 0) { - warn("recv(%d, nick)", user->fd); - remove_user(user); + if ((cfd = accept(sfd, NULL, NULL)) < 0) { + warn("accept(%d)", cfd); continue; } - - user->id = ++ids; - user->nplays = 0; - user->nwins = 0; - user->opponent = NULL; - TAILQ_INSERT_TAIL(&users, user, next); - - (*nusers)++; - - printf("%s: active users=%d\n", getprogname(), *nusers); - printf("%s: client connected: %s#%d (fd=%d)\n", - getprogname(), user->name, user->id, user->fd); /* * Create a child process to serve the client so the parent can * continue waiting for another client to serve. @@ -327,18 +375,21 @@ main(int argc, char *argv[]) case -1: err(1, "fork"); case 0: - if (srv(user) < 0) - warnx("srv(%s#%d) failed", user->name, user->id); - (*nusers)--; + if (srv(cfd) < 0) + warnx("srv(%d) failed", cfd); _exit(0); default: - close(user->fd); + close(cfd); } } /* Will get here only if a termination signal is caught. */ close(sfd); - remove_user(user); + for (i = 0; i < CLIENTS_MAX; i++) + remove_client(&clients[i]); + munmap(ids, sizeof(*ids)); + munmap(nclients, sizeof(*nclients)); + munmap(clients, sizeof(struct client) * CLIENTS_MAX); return (0); }