#include #include #include #include #include #include #include #include #include #include #include /* * simple kqueue chat server by Peter Werner */ #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); }