summaryrefslogtreecommitdiff
path: root/app/v3_c/examples/kqueue_chat.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/v3_c/examples/kqueue_chat.c')
-rw-r--r--app/v3_c/examples/kqueue_chat.c201
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);
+}
+