read the source · systems/03_udp_pingpong.c
#include "../tools/bench.h"
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <pthread.h>
#include <sys/epoll.h>
#include <sys/socket.h>
#include <unistd.h>
#define ROUNDS 50000
enum { BLOCK, EPOLL, SPIN };
static int mk(int port, int nonblock) {
int fd = socket(AF_INET, SOCK_DGRAM | (nonblock ? SOCK_NONBLOCK : 0), 0);
struct sockaddr_in a = { .sin_family = AF_INET, .sin_port = htons(port), .sin_addr.s_addr = htonl(INADDR_LOOPBACK) };
if (bind(fd, (struct sockaddr *)&a, sizeof a)) { perror("bind"); exit(1); }
return fd;
}
/* Wait for a readable socket in the chosen style, then read one datagram. */
static ssize_t rx(int fd, int ep, int mode, char *buf, size_t n, struct sockaddr_in *from) {
socklen_t fl = sizeof *from;
if (mode == BLOCK) return recvfrom(fd, buf, n, 0, (struct sockaddr *)from, &fl);
if (mode == EPOLL) { struct epoll_event ev; while (epoll_wait(ep, &ev, 1, -1) < 1) {} return recvfrom(fd, buf, n, 0, (struct sockaddr *)from, &fl); }
for (;;) { ssize_t r = recvfrom(fd, buf, n, MSG_DONTWAIT, (struct sockaddr *)from, &fl); if (r >= 0 || errno != EAGAIN) return r; }
}
static int g_mode;
static void *server(void *arg) {
bench_pin(4);
int fd = mk(40001, g_mode != BLOCK), ep = -1; char buf[64]; struct sockaddr_in from;
if (g_mode == EPOLL) { ep = epoll_create1(0); struct epoll_event ev = { .events = EPOLLIN, .data.fd = fd }; epoll_ctl(ep, EPOLL_CTL_ADD, fd, &ev); }
for (int i = 0; i < ROUNDS; i++) { ssize_t r = rx(fd, ep, g_mode, buf, sizeof buf, &from); sendto(fd, buf, (size_t)r, 0, (struct sockaddr *)&from, sizeof from); }
close(fd); (void)arg; return NULL;
}
int main(int argc, char **argv) {
const char *m = argc > 1 ? argv[1] : "block";
g_mode = !strcmp(m, "epoll") ? EPOLL : !strcmp(m, "spin") ? SPIN : BLOCK;
pthread_t th; pthread_create(&th, NULL, server, NULL); usleep(20000);
bench_pin(2);
int fd = mk(40002, g_mode != BLOCK), ep = -1; char buf[64] = "ping"; struct sockaddr_in srv = { .sin_family = AF_INET, .sin_port = htons(40001), .sin_addr.s_addr = htonl(INADDR_LOOPBACK) }, from;
if (g_mode == EPOLL) { ep = epoll_create1(0); struct epoll_event ev = { .events = EPOLLIN, .data.fd = fd }; epoll_ctl(ep, EPOLL_CTL_ADD, fd, &ev); }
uint64_t *s = malloc(ROUNDS * sizeof *s); memset(s, 0xFF, ROUNDS * sizeof *s);
for (int i = 0; i < ROUNDS; i++) {
uint64_t t0 = bench_ns();
sendto(fd, buf, 16, 0, (struct sockaddr *)&srv, sizeof srv);
rx(fd, ep, g_mode, buf, sizeof buf, &from);
s[i] = bench_ns() - t0;
}
pthread_join(th, NULL);
bench_stats st = bench_compute(s + 1000, ROUNDS - 1000); /* drop warm-up */
bench_print_header("round-trip ns"); bench_print(m, &st);
return 0;
}