JMU
UDP Socket Programming
An Introduction with Examples in C


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Review
UDP Programming
Creating a Socket: socket() socket
int socket(int domain, int type, int protocol)
Purpose:
Create a socket
Details:
domain AF_INET or AF_INET6 for IP
type SOCK_DGRAM for UDP
protocol 0 for UDP over IP
Return The file descriptor on success; -1 on error
#include <sys/socket.h> sys/socket.h
Setting-Up for Receiving
Binding to an Address and/or Port: bind() bind
int bind(int fd, const struct sockaddr *addr, socklen_t addrlen)
Purpose:
Associate a socket with a particular address and/or port
Details:
fd The file descriptor of a socket
addr The address/port to bind to
addrlen The length of the address
Return 0 on success; -1 on error
#include <sys/socket.h> sys/socket.h
Receiving a Datagram: recvfrom() recvfrom
ssize_t recvfrom(int fd, void *buffer, size_t buflen, int flags, struct sockaddr *src_addr, socklen_t *addrlen)
Purpose:
Receive a UDP datagram
Details:
fd The file descriptor of the UDP socket
buffer The buffer to fill with the datagram's payload
length The length of the buffer
flags Flags (often 0)
src_addr The address the datagram was sent from
addrlen The length of the source's address
Return The number of bytes received on success; -1 on error
#include <sys/socket.h> sys/socket.h
A Simple Sequential Text "Receiver"
unixexamples/udp/weather_recorder.c
        #include <arpa/inet.h>  // For byte order conversions
#include <string.h>     // For memset()
#include <sys/socket.h>
#include <unistd.h>
#include "weather.h"

int
main(void)
{
  char    buffer[STATION_SIZE+TEMPERATURE_SIZE];
  int     fd, keep_going;
  struct sockaddr_in   address;
  
  // Create a IP4/UDP socket
  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address (of this host)
  memset(&address, 0, sizeof(address));
  address.sin_family = AF_INET;
  address.sin_port = htons(TEMPERATURE_PORT);
  address.sin_addr.s_addr = htonl(INADDR_ANY); // In case the host has multiple

  // Bind the socket to the address
  bind(fd, (struct sockaddr *)&address, sizeof(struct sockaddr));

  keep_going = 1;
  while (keep_going)
    {
      // Receive the message (we don't care about the address of the sender)
      recvfrom(fd, buffer, STATION_SIZE+TEMPERATURE_SIZE, 0, NULL, NULL) ; 
      
      if (buffer[3] == '!')
        {
          keep_going = 0;
        }
      else
        {
          write(STDOUT_FILENO, "Report from ", 12);
          write(STDOUT_FILENO, buffer, STATION_SIZE);
          write(STDOUT_FILENO, ": ", 2);
          write(STDOUT_FILENO, buffer+STATION_SIZE, TEMPERATURE_SIZE); 
          write(STDOUT_FILENO, "\n\n", 2);
        }
    }
  
  close(fd);
}

        
A Simple Sequential Text "Receiver" (cont.)
Setting-Up for Sending
Sending a Datagram: sendto() sendto
ssize_t sendto(int fd, const void *buffer, size_t buflen, int flags, const struct sockaddr *dest_addr, socklen_t *addrlen)
Purpose:
Send a UDP datagram to a particular address and port
Details:
fd The file descriptor of the UDP socket
buffer The datagram's payload
length The length of the payload
flags Flags (often 0)
dest_addr The address to send the datagram to
addrlen The length of the destination address
Return The number of bytes sent on success; -1 on error
#include <sys/socket.h> sys/socket.h
A Simple Text "Sender"
unixexamples/udp/weather_station.c
        #include <arpa/inet.h>  // For byte order conversions
#include <string.h>     // For memset()
#include <sys/socket.h>
#include <unistd.h>
#include "weather.h"

int
main(int argc, char *argv[])
{
  char    buffer[STATION_SIZE+TEMPERATURE_SIZE];
  char    station[] = "JMU";
  char    temperature[TEMPERATURE_SIZE+1];
  int     fd, keep_going;
  ssize_t input_length;
  struct sockaddr_in   destination;

  if (argc > 1) strncpy(station, argv[1], STATION_SIZE);

  // Create a IP4/UDP socket
  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address of the receiver
  memset(&destination, 0, sizeof(struct sockaddr_in));
  destination.sin_family = AF_INET;
  inet_pton(AF_INET, "134.126.125.234", &(destination.sin_addr));
  destination.sin_port = htons(TEMPERATURE_PORT);
    

  keep_going = 1;
  while (keep_going)
    {
      write(STDOUT_FILENO, "Temperature (or [Enter] to QUIT): ", 35);

      // Clear the outgoing payload
      memset(buffer, 0, STATION_SIZE+TEMPERATURE_SIZE);

      // Populate the outgoing payload
      strncpy(buffer, station, 3);
      input_length = read(STDIN_FILENO, temperature, TEMPERATURE_SIZE+1);
      if (input_length > 1)
        {
          strncpy(buffer+3, temperature, input_length-1);
        }
      else
        {
          strncpy(buffer+3, "!!!!!", 5);
          keep_going = 0;
        }

      // Send the datagram
      sendto(fd, buffer, STATION_SIZE+TEMPERATURE_SIZE, 0,
             (struct sockaddr *)&destination, sizeof(destination)) ;
    }

  close(fd);
}

        
Sending/Receiving and Clients/Servers
A Server that Receives and Sends
A Server that Receives and Sends (cont.)
unixexamples/udp/forecast_server.c
        #include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>   
#include <sys/socket.h>
#include <unistd.h>

#include "weather.h"

int
main(void)
{
  char    in[STATION_SIZE], out[TEMPERATURE_SIZE];
  int     fd;
  struct sockaddr_in address, client_address;
  socklen_t client_length = sizeof(struct sockaddr);
  
  // Create an IP4/UDP socket
  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address of this host
  memset(&address, 0, sizeof(address));
  address.sin_family = AF_INET;
  address.sin_port = htons(FORECAST_PORT);
  address.sin_addr.s_addr = htonl(INADDR_ANY); // In case the host has multiple

  // Bind the socket to the address
  bind(fd, (struct sockaddr *)&address, sizeof(struct sockaddr));

  // Process requests
  while (1)
    {
      // Receive the request
      recvfrom(fd, in, STATION_SIZE, 0,
               (struct sockaddr *)&client_address, &client_length);

      // Send the response
      memset(&out, 0, TEMPERATURE_SIZE);
      sprintf(out, "%5.1f", forecast_for(in));
      sendto(fd, out, TEMPERATURE_SIZE, 0,
             (struct sockaddr *)&client_address, client_length);
    }

  close(fd);
}
        
A Client that Sends and Receives
unixexamples/udp/forecast_client.c
        #include <arpa/inet.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>
#include "weather.h"

int
main(int argc, char *argv[])
{
  char    in[TEMPERATURE_SIZE];
  char    out[] = "JMU";
  int     fd;
  struct sockaddr_in   server_address;

  if (argc > 1) strncpy(out, argv[1], STATION_SIZE);

  // Create an IP4/UDP socket
  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address of the server
  memset(&server_address, 0, sizeof(struct sockaddr_in));
  server_address.sin_family = AF_INET;
  inet_pton(AF_INET, "134.126.125.234", &(server_address.sin_addr));
  server_address.sin_port = htons(FORECAST_PORT);

  // Send the request
  sendto(fd, out, STATION_SIZE, 0,
         (struct sockaddr *)&server_address, sizeof(server_address)) ;

  // Receive the response
  recvfrom(fd, in, TEMPERATURE_SIZE, 0, NULL, NULL); 

  // Display the respons
  write(STDOUT_FILENO, "Forecast: ", 10);
  write(STDOUT_FILENO, in, TEMPERATURE_SIZE);
  write(STDOUT_FILENO, "\n", 1);

  close(fd);
}

        
Message Formats
A struct for a Binary Message
unixexamples/udp/weather.h
        #ifndef WEATHER_H
#define WEATHER_H

#define FORECAST_PORT        22801
#define RANGE_PORT           22802
#define TEMPERATURE_PORT     22807
#define TEMPERATURE_SIZE     5
#define STATION_SIZE         3

struct range {
  uint16_t low;
  uint16_t high;
};

float
forecast_for(char *station);

void
hton_range(const struct range *h, struct range *n);

void
ntoh_range(const struct range *n, struct range *h);



#endif
        
A Server with a Binary Message
unixexamples/udp/range_server.c
        #include <arpa/inet.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>   
#include <sys/socket.h>
#include <unistd.h>
#include "weather.h"


int
main(void)
{
  char    in[STATION_SIZE];
  int     fd;
  struct range current, out;
  struct sockaddr_in address, client_address;
  socklen_t client_length = sizeof(struct sockaddr);
  
  // Create a IP4/UDP socket
  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address (of this host)
  memset(&address, 0, sizeof(address));
  address.sin_family = AF_INET;
  address.sin_port = htons(RANGE_PORT);
  address.sin_addr.s_addr = htonl(INADDR_ANY); // In case the host has multiple

  // Bind the socket to the address
  bind(fd, (struct sockaddr *)&address, sizeof(struct sockaddr));

  // Process requests
  while (1)
    {
      // Receive the request
      recvfrom(fd, in, STATION_SIZE, 0,
               (struct sockaddr *)&client_address, &client_length);
      
      // Create the response
      current.low  = (uint16_t)forecast_for(in);
      current.high = (uint16_t)forecast_for(in);

      // Convert the respons to network byte order
      hton_range(&current, &out);

      // Send the response
      sendto(fd, &out, sizeof(struct range), 0,
             (struct sockaddr *)&client_address, client_length);
    }

  close(fd);
}
        
A Client with a Binary Message
unixexamples/udp/range_client.c
        #include <arpa/inet.h>  // For byte order conversions
#include <stdio.h>
#include <string.h>     // For memset()
#include <sys/socket.h>
#include <unistd.h>
#include "weather.h"

int
main(int argc, char *argv[])
{
  char    out[] = "JMU";
  int     fd;
  struct range         current, in;
  struct sockaddr_in   server_address;

  
  if (argc > 1) strncpy(out, argv[1], STATION_SIZE);

  // Create a IP4/UDP socket
  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address of the server
  memset(&server_address, 0, sizeof(struct sockaddr_in));
  server_address.sin_family = AF_INET;
  inet_pton(AF_INET, "134.126.125.234", &(server_address.sin_addr));
  server_address.sin_port = htons(RANGE_PORT);

  // Send the request
  sendto(fd, out, STATION_SIZE, 0,
         (struct sockaddr *)&server_address, sizeof(server_address)) ;
  
  // Receive the response
  recvfrom(fd, &in, TEMPERATURE_SIZE, 0, NULL, NULL); 

  // Convert from network byte order to host byte order
  ntoh_range(&in, &current);

  // DIsplay the converted response
  printf("Low:  %d\n", current.low);
  printf("High: %d\n", current.high);

  close(fd);
}

        
Sequential and Concurrent Servers
Sequential and Concurrent Servers (cont.)
A Server with Multiple Processes
unixexamples/udp/multiprocessed_forecast_server.c
        #include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>   
#include <sys/socket.h>
#include <unistd.h>

#include "weather.h"

int
main(void)
{
  char    in[STATION_SIZE], out[TEMPERATURE_SIZE];
  int     ifd, ofd, pid;
  struct sockaddr_in address, client_address;
  socklen_t client_length = sizeof(struct sockaddr);
  
  // Create an IP4/UDP socket
  ifd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address of this host
  memset(&address, 0, sizeof(address));
  address.sin_family = AF_INET;
  address.sin_port = htons(FORECAST_PORT);
  address.sin_addr.s_addr = htonl(INADDR_ANY); // In case the host has multiple

  // Bind the socket to the address
  bind(ifd, (struct sockaddr *)&address, sizeof(struct sockaddr));
  
  // Handle requests (each in its own child process)
  while (1)
    {
      // Receive the request
      recvfrom(ifd, in, STATION_SIZE, 0,
               (struct sockaddr *)&client_address, &client_length);

      // Create the child process
      pid = fork();
      if (pid == 0)
        {
          // Close the child's copy of the incoming socket
          close (ifd); 

          // Send the response
          memset(&out, 0, TEMPERATURE_SIZE);
          sprintf(out, "%5.1f", forecast_for(in));
          ofd = socket(AF_INET, SOCK_DGRAM, 0);
          sendto(ofd, out, TEMPERATURE_SIZE, 0,
                 (struct sockaddr *)&client_address, client_length);
          close(ofd);
          exit(0);
        }
    }
  
  close(ifd);
}
        
A Server with Multiple Threads
unixexamples/udp/threaded_forecast_server.c
        #include <arpa/inet.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>   
#include <sys/socket.h>
#include <unistd.h>

#include "weather.h"


// The arguments needed to send a response
struct thread_arguments {
  char            station[STATION_SIZE];
  struct sockaddr client_address;
  socklen_t       client_length;
};


// The entry point for the threads
static void
*send_response(void *arg)
{
  char  out[TEMPERATURE_SIZE];
  int   fd;
  struct thread_arguments *args;

  // Cast the arguments
  args = (struct thread_arguments *)arg;

  memset(&out, 0, TEMPERATURE_SIZE);
  sprintf(out, "%5.1f", forecast_for(args->station));

  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  sendto(fd, out, TEMPERATURE_SIZE, 0, &(args->client_address), args->client_length);
  close(fd);

  // Free the memory used by the arguments
  // (Caller Allocates/Callee Frees)
  free(args);
  
  return NULL;
}

  


int
main(void)
{
  char    in[STATION_SIZE];
  int     fd;
  pthread_t  helper;
  struct sockaddr_in address, client_address;
  struct thread_arguments *args;
  socklen_t client_length = sizeof(struct sockaddr);
  
  // Create an IP4/UDP socket
  fd = socket(AF_INET, SOCK_DGRAM, 0); 
  
  // Initialize the address of this host
  memset(&address, 0, sizeof(address));
  address.sin_family = AF_INET;
  address.sin_port = htons(FORECAST_PORT);
  address.sin_addr.s_addr = htonl(INADDR_ANY); // In case the host has multiple

  // Bind the socket to the address
  bind(fd, (struct sockaddr *)&address, sizeof(struct sockaddr));

  // Process requests
  while (1)
    {
      // Receive the request
      recvfrom(fd, in, STATION_SIZE, 0,
               (struct sockaddr *)&client_address, &client_length);

      // Make a copy of the message and the client address
      // for the thread that will handle the response
      // (Caller Allocates/Callee Frees)
      args = (struct thread_arguments *)malloc(sizeof(struct thread_arguments));
      strncpy(args->station, in, STATION_SIZE);
      memcpy(&(args->client_address), &client_address, client_length);
      args->client_length  = client_length;

      // Create a thread to handle the response
      pthread_create(&helper, NULL, send_response, (void *)args);
      pthread_detach(helper);
    }
  
  close(fd);
}
        
Improving Concurrent Servers
Socket Options: setsockopt() setsockopt
int setsockopt(int fd, int level, int optname, const void *optval, socklen_t optlen)
Purpose:
Set socket options
Details:
fd The file descriptor of the UDP socket
level SOL_SOCKET, IPPROTO_IP, ...
optname The option to set
optval The value of the option
optlen The size of the optval argument
Return 0 on success; -1 on error
#include <sys/socket.h> sys/socket.h
Socket Options (cont.)
Socket Options (cont.)
A Partial Example
  struct timeval timeout;
  timeout.tv_sec  = 1;
  timeout.tv_usec = 0;

  setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, &timeout, sizeof(struct timeval));