Created
August 21, 2015 08:02
-
-
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
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* 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