Recently I wanted to send some Shiny usage data from R to a certain metrics server. Since R includes write.socket() and friends for opening arbitrary network sockets, it seemed at the outset that this would be quite simple. However, I ran into an interesting roadblock along the way.

It turns out that R’s socket API only supports TCP connections, which you can confirm by looking at the source code – and in this case I needed to send UDP packets instead. This was a little surprising to me, since most other languages would include UDP support out of the box; it’s a core internet protocol, after all. For whatever reason, this seems not to be the case with R, and even after searching CRAN and GitHub I wasn’t able to find an existing package that provides UDP socket support.

To remedy this, I put together a simple way to write messages to UDP sockets from R.

Since working with sockets is done at the system level, it makes sense to use R’s C API in this case. The following is a self-contained implementation that you can drop into an R package’s src directory as udp.c :

#include <Rinternals.h> #include <R_ext/Rdynload.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/socket.h> #include <arpa/inet.h> SEXP udp_send_impl ( SEXP message , SEXP host , SEXP port ) { const char * msg = CHAR ( asChar ( message )); const char * host_ = CHAR ( asChar ( host )); int port_ = asInteger ( port ); // Open the socket. int sock ; struct sockaddr_in server ; if (( sock = socket ( AF_INET , SOCK_DGRAM , IPPROTO_UDP )) < 0 ) { Rf_error ( "Failed to create UDP socket." ); return R_NilValue ; } server . sin_family = AF_INET ; server . sin_addr . s_addr = INADDR_ANY ; server . sin_port = htons (( short ) port_ ); if ( inet_aton ( host_ , & server . sin_addr ) == 0 ) { Rf_error ( "Failed to parse host address." ); close ( sock ); return R_NilValue ; } // Send message. if ( sendto ( sock , msg , strlen ( msg ), 0 , ( struct sockaddr * ) & server , sizeof ( server )) < 0 ) { Rf_error ( "Failed to send message." ); } close ( sock ); return R_NilValue ; } static const R_CallMethodDef udp_entries [] = { { "udp_send_impl" , ( DL_FUNC ) & udp_send_impl , 3 }, { NULL , NULL , 0 } }; void R_init_udp ( DllInfo * info ) { R_registerRoutines ( info , NULL , udp_entries , NULL , NULL ); R_useDynamicSymbols ( info , FALSE ); }

Note that you may need to adjust the R_registerRoutines() code if you have existing C or C++ code in your package.

To use this C function from R, you’ll need something like the following in your package:

#' @export #' @useDynLib udp udp_send_impl udp_send <- function ( message , host = "localhost" , port ) { stopifnot ( is.character ( message )) stopifnot ( is.character ( host )) stopifnot ( is.numeric ( port )) .Call ( udp_send_impl , message , host , as.integer ( port )) invisible ( NULL ) }

To confirm that this works, you can listen for UDP packets with from a terminal with

$ nc -lu 1001

and you can send UDP packets to that server from R with