Skip to content

Instantly share code, notes, and snippets.

@josephg
Created August 26, 2017 08:20
Show Gist options
  • Save josephg/6c078a241b0e9e538ac04ef28be6e787 to your computer and use it in GitHub Desktop.
Save josephg/6c078a241b0e9e538ac04ef28be6e787 to your computer and use it in GitHub Desktop.
kqueue network & file example
#include <sys/socket.h>
#include <sys/un.h>
#include <sys/event.h>
#include <netdb.h>
#include <assert.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
int main(int argc, const char * argv[]) {
// Macos automatically binds both ipv4 and 6 when you do this.
struct sockaddr_in6 addr = {};
addr.sin6_len = sizeof(addr);
addr.sin6_family = AF_INET6;
addr.sin6_addr = in6addr_any; //(struct in6_addr){}; // 0.0.0.0 / ::
addr.sin6_port = htons(9999);
int localFd = socket(addr.sin6_family, SOCK_STREAM, 0);
assert(localFd != -1);
int on = 1;
setsockopt(localFd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(localFd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
perror("bind");
return 1;
}
assert(listen(localFd, 5) != -1);
int kq = kqueue();
struct kevent evSet;
EV_SET(&evSet, localFd, EVFILT_READ, EV_ADD, 0, 0, NULL);
assert(-1 != kevent(kq, &evSet, 1, NULL, 0, NULL));
int junk = open("some.big.file", O_RDONLY);
uint64_t bytes_written = 0;
struct kevent evList[32];
while (1) {
// returns number of events
int nev = kevent(kq, NULL, 0, evList, 32, NULL);
// printf("kqueue got %d events\n", nev);
for (int i = 0; i < nev; i++) {
int fd = (int)evList[i].ident;
if (evList[i].flags & EV_EOF) {
printf("Disconnect\n");
close(fd);
// Socket is automatically removed from the kq by the kernel.
} else if (fd == localFd) {
struct sockaddr_storage addr;
socklen_t socklen = sizeof(addr);
int connfd = accept(fd, (struct sockaddr *)&addr, &socklen);
assert(connfd != -1);
// Listen on the new socket
EV_SET(&evSet, connfd, EVFILT_READ, EV_ADD, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
printf("Got connection!\n");
int flags = fcntl(connfd, F_GETFL, 0);
assert(flags >= 0);
fcntl(connfd, F_SETFL, flags | O_NONBLOCK);
// schedule to send the file when we can write (first chunk should happen immediately)
EV_SET(&evSet, connfd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, NULL);
kevent(kq, &evSet, 1, NULL, 0, NULL);
} else if (evList[i].filter == EVFILT_READ) {
// Read from socket.
char buf[1024];
size_t bytes_read = recv(fd, buf, sizeof(buf), 0);
printf("read %zu bytes\n", bytes_read);
} else if (evList[i].filter == EVFILT_WRITE) {
// printf("Ok to write more!\n");
off_t offset = (off_t)evList[i].udata;
off_t len = 0;//evList[i].data;
if (sendfile(junk, fd, offset, &len, NULL, 0) != 0) {
// perror("sendfile");
// printf("err %d\n", errno);
if (errno == EAGAIN) {
// schedule to send the rest of the file
EV_SET(&evSet, fd, EVFILT_WRITE, EV_ADD | EV_ONESHOT, 0, 0, (void *)(offset + len));
kevent(kq, &evSet, 1, NULL, 0, NULL);
}
}
bytes_written += len;
printf("wrote %lld bytes, %lld total\n", len, bytes_written);
}
}
}
return 0;
}
@iradization
Copy link

Hi Joseph,

I'm using macOS and looking for a way to get notification on every new socket and check it's info (ip, port, direction, etc...)

I wonder if kqueue provide this information (I only care about sockets, not files, directories, pipes etc.. ).

So far I thought it's possible but in your example you wrote "Socket is automatically removed from the kq by the kernel" ..

Perhaps do you if there's any configuration I can use to do so using kq ?

thanks !
Irad

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment