From f72fba0af15802be8a4da4168189e35ada8abc59 Mon Sep 17 00:00:00 2001 From: Dov Murik Date: Mon, 26 Oct 2015 16:12:42 -0400 Subject: [PATCH] udp-echo-server: send the echo response when socket is writable Instead of calling sendto() directly from the udp_cb callback (which is called when a new packet is received), prepare a response and tell libev to call our udp_response_cb callback when the socket is writable. This also removes all the global variables. --- src/udp-echo-server.c | 52 ++++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 10 deletions(-) diff --git a/src/udp-echo-server.c b/src/udp-echo-server.c index 02f1e13..0f69a63 100644 --- a/src/udp-echo-server.c +++ b/src/udp-echo-server.c @@ -13,29 +13,61 @@ #define DEFAULT_PORT 3333 #define BUF_SIZE 4096 -// Lots of globals, what's the best way to get rid of these? -int sd; // socket descriptor -struct sockaddr_in addr; -int addr_len = sizeof(addr); -char buffer[BUF_SIZE]; +typedef struct { + ev_io watcher; + struct sockaddr addr; + socklen_t addr_len; + char* buffer; + size_t buffer_len; +} response_details; + +// This callback is called when data is writable on the UDP socket, with +// w->data pointing to a response_details instance. +static void udp_response_cb(EV_P_ ev_io *w, int revents) { + response_details *r = w->data; + + // Echo the buffer back + sendto(w->fd, r->buffer, r->buffer_len, 0, &r->addr, r->addr_len); + + // Tell libev not to call this watcher again, because we're freeing it from memory + ev_io_stop(EV_A_ w); + + free(r->buffer); + free(r); +} // This callback is called when data is readable on the UDP socket. static void udp_cb(EV_P_ ev_io *w, int revents) { + struct sockaddr_in addr; + int addr_len = sizeof(addr); + char buffer[BUF_SIZE]; + response_details *r; + puts("udp socket has become readable"); - socklen_t bytes = recvfrom(sd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*) &addr, (socklen_t *) &addr_len); + socklen_t bytes = recvfrom(w->fd, buffer, sizeof(buffer) - 1, 0, (struct sockaddr*) &addr, (socklen_t *) &addr_len); // add a null to terminate the input, as we're going to use it as a string buffer[bytes] = '\0'; printf("udp client said: %s", buffer); - // Echo it back. - // WARNING: this is probably not the right way to do it with libev. - // Question: should we be setting a callback on sd becomming writable here instead? - sendto(sd, buffer, bytes, 0, (struct sockaddr*) &addr, sizeof(addr)); + // Prepare a response_details struct to be passed as w->data to the + // udp_response_cb callback (that will be called when the socket is writable). + r = calloc(1, sizeof(response_details)); + memcpy(&r->addr, &addr, sizeof(addr)); + r->addr_len = addr_len; + r->buffer = calloc(bytes, sizeof(char)); + memcpy(r->buffer, buffer, bytes); + r->buffer_len = bytes; + + ev_io_init(&r->watcher, udp_response_cb, w->fd, EV_WRITE); + r->watcher.data = r; + ev_io_start(EV_A_ &r->watcher); } int main(void) { + struct sockaddr_in addr; + int sd; // socket descriptor int port = DEFAULT_PORT; puts("udp_echo server started...");