Skip to content

Instantly share code, notes, and snippets.

@neeraj9
Created August 21, 2015 08:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save neeraj9/fc28c4d3f7950fe74b6e to your computer and use it in GitHub Desktop.
Save neeraj9/fc28c4d3f7950fe74b6e to your computer and use it in GitHub Desktop.
kqueue gives back write event on a listen socket eventhough I registered for read
/* http://doc.geoffgarside.co.uk/kqueue/files/chatserv.c */
#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("0.0.0.0", 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;
printf("ke.flags = %d\n", ke.flags);
/*
* since we only have one kevent in the eventlist, we're only
* going to get one event at a time
*/
/* original was (TODO) and worked, because ke.flags is EVFILT_WRITE
if (ke.ident == servsock) {
*/
if ((ke.flags == EVFILT_READ) && (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);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment