diff options
Diffstat (limited to 'app/v3_c')
-rw-r--r-- | app/v3_c/examples/kqueue_chat.c | 201 |
1 files changed, 201 insertions, 0 deletions
diff --git a/app/v3_c/examples/kqueue_chat.c b/app/v3_c/examples/kqueue_chat.c new file mode 100644 index 0000000..b9d913c --- /dev/null +++ b/app/v3_c/examples/kqueue_chat.c @@ -0,0 +1,201 @@ +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/event.h> +#include <sys/time.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> + +/* + * simple kqueue chat server by Peter Werner <peterw@ifost.org.au> + */ + +#define NUSERS 10 + +/* + * we store each connected clients filedescriptor + * and ip address in an array, which places an upper + * bound of 10 users. This is done for simplicity. + */ + +struct uc { + int uc_fd; + char *uc_addr; +} users[NUSERS]; + +/* + * bind(2) and listen(2) to a tcp port + */ +int +mksock(char *addr, int port) +{ + int i, sock; + struct sockaddr_in serv; + + sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock == -1) + err(1, "socket"); + + i = 1; + if (setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, (void *)&i, + (socklen_t)sizeof(i)) == -1) + warn("setsockopt"); + + memset(&serv, 0, sizeof(struct sockaddr_in)); + serv.sin_family = AF_INET; + serv.sin_port = htons(port); + serv.sin_addr.s_addr = inet_addr(addr); + + i = bind(sock, (struct sockaddr *)&serv, (socklen_t)sizeof(serv)); + if (i == -1) + err(1, "bind"); + + i = listen(sock, 5); + if (i == -1) + err(1, "listen"); + + return(sock); +} + +int +main(void) +{ + int servsock, kq, i, uidx; + struct kevent ke; + struct sockaddr_in c; /* client */ + socklen_t len; + char buf[1024]; + char *umsg = "too many users!\n"; + + /* get a listening socket */ + servsock = mksock("127.0.0.1", 5000); + + /* get our kqueue descriptor */ + kq = kqueue(); + if (kq == -1) + err(1, "kqueue!"); + + memset(&ke, 0, sizeof(struct kevent)); + memset(users, 0, sizeof(struct uc) * NUSERS); + + /* fill out the kevent struct */ + EV_SET(&ke, servsock, EVFILT_READ, EV_ADD, 0, 5, NULL); + + /* set the event */ + i = kevent(kq, &ke, 1, NULL, 0, NULL); + if (i == -1) + err(1, "set kevent"); + + while (1) { + + memset(&ke, 0, sizeof(ke)); + + /* receive an event, a blocking call as timeout is NULL */ + i = kevent(kq, NULL, 0, &ke, 1, NULL); + if (i == -1) + err(1, "kevent!"); + + if (i == 0) + continue; + + /* + * since we only have one kevent in the eventlist, we're only + * going to get one event at a time + */ + + if (ke.ident == servsock) { + + /* server socket, theres a client to accept */ + + len = (socklen_t)sizeof(c); + i = accept(servsock, (struct sockaddr *)&c, &len); + if (i == -1) + err(1, "accept!"); + + for (uidx = 0; uidx < NUSERS; uidx++) + if (users[uidx].uc_fd == 0) + break; + + if (uidx == NUSERS) { + warnx("%s", umsg); + write(i, umsg, strlen(umsg)); + close(i); + continue; + } + + users[uidx].uc_fd = i; /* users file descriptor */ + users[uidx].uc_addr = strdup(inet_ntoa(c.sin_addr)); + if (users[uidx].uc_addr == NULL) + err(1, "strdup"); + + EV_SET(&ke, i, EVFILT_READ, EV_ADD, 0, 0, NULL); + i = kevent(kq, &ke, 1, NULL, 0, NULL); + if (i == -1) + err(1, "kevent add user!"); + + printf("connection from %s added\n", + users[uidx].uc_addr); + } else { + + /* + * got a message to distribute, first find the user + * and read their message + */ + + for (uidx = 0; uidx < NUSERS; uidx++) + if (users[uidx].uc_fd == ke.ident) + break; + + if (uidx == NUSERS) + errx(1, "bogus message!"); + + memset(buf, 0, sizeof(buf)); + + i = read(users[uidx].uc_fd, buf, sizeof(buf)); + if (i == -1) + continue; + + if (i == 0) { /* EOF from a client */ + + printf("removing %s\n", users[uidx].uc_addr); + + EV_SET(&ke, users[uidx].uc_fd, EVFILT_READ, + EV_DELETE, 0, 0, NULL); + + i = kevent(kq, &ke, 1, 0, 0, NULL); + if (i == -1) + err(1, "rm user from kq"); + + close(users[uidx].uc_fd); + free(users[uidx].uc_addr); + + users[uidx].uc_fd = 0; + users[uidx].uc_addr = NULL; + + continue; + } + + printf("got a message from %s\n", users[uidx].uc_addr); + + /* now write it to the other users */ + + for (uidx = 0; uidx < NUSERS; uidx++) { + + if (users[uidx].uc_fd == 0 || + users[uidx].uc_fd == ke.ident) + continue; + + i = write(users[uidx].uc_fd, buf, sizeof(buf)); + if (i == -1) + warn("write failed!"); + } + } /* end if */ + } /* end while */ + + return(0); +} + |