Epoll looks like the best networking API to me. As far as I understand, it's the fastest API that uses regular file descriptors, and the API is very simple and straightforward.
I compiled all the relevant components into this example, I haven't tested it though:
static void epoll_add_socket (int epollfd, int socketfd, uint32_t events_to_watch_for) {
struct epoll_event event = {
.events = events_to_watch_for,
.data.fd = socketfd,
};
epoll_ctl(epollfd, EPOLL_CTL_ADD, socketfd, &event);
}
static void epoll_remove_socket (int epollfd, int socketfd) {
epoll_ctl(epollfd, EPOLL_CTL_DEL, socketfd, NULL);
}
static void make_socket_nonblocking (int socketfd) {
fcntl(socketfd, F_SETFD, fcntl(socketfd, F_GETFD, 0) | O_NONBLOCK);
}
int main (int argc, char *argv[]) {
int listen_socket = socket(AF_INET, SOCK_STREAM, 0);
struct sockaddr_in server_addr = {
.sin_family = AF_INET,
.sin_addr.s_addr = INADDR_ANY,
.sin_port = htons(8080),
};
bind(listen_socket, (struct sockaddr*)&server_addr, sizeof(server_addr));
make_socket_nonblocking(listen_socket);
listen(listen_socket, 32);
int epollfd = epoll_create1(0);
epoll_add_socket(epollfd, listen_socket, EPOLLIN);
#define MAX_EVENTS 64
struct epoll_event events [MAX_EVENTS];
while (1) {
int eventcount = epoll_wait(epollfd, events, MAX_EVENTS, -1);
for (int i=0; i<eventcount; i++) {
if (events[i].data.fd == listen_socket) {
struct sockaddr_in client_addr;
int socket_len = sizeof(client_addr);
accept(listen_socket, (struct sockaddr *)&client_addr, &socket_len);
make_socket_nonblocking(client_socket);
epoll_add_socket(epollfd, client_socket, EPOLLIN | EPOLLOUT | EPOLLRDHUP | EPOLLHUP);
continue;
}
if (events[i].events & (EPOLLRDHUP | EPOLLHUP)) {
epoll_remove_socket(epollfd, events[i].data.fd);
}
else if (events[i].events & EPOLLIN) {
}
else if (events[i].events & EPOLLOUT) {
}
}
}
return 0;
}