Menu

Class 23: Networks VI, a UDP Server


Reading
APUE 16.5, especially the connection-less client and server examples.

Homework
Printout the Homework and answer the questions on that paper.

UDP vs. TCP
Recall that UDP is a protocol for communication via datagrams rather than byte-streams. It is connectionless and unreliable, but ... there's less overhead involved. For some applications, UDP is a better approach. This class is about UDP server programs. The previous class covered UDP client programs.

The big difference in code
As we saw last class, there are two big differences in code between UDP and TCP.
  1. The second argument to the socket system call is SOCK_DGRAM instead of SOCK_STREAM, and
  2. data is not sent/recieved with the familiar read/write system calls (or fscanf/fprintf C IO calls).
Obviously, point 1 is trivial. What about point 2? Fundamentally, read/write/fscanf/fprintf all assume a file descriptor that provides a stream-of-characters ... precisely what we don't have for UDP. With UDP each datagram needs to be sent with a destination address, so whatever function allows us to send data is going to need extra parameters for passing the host+port. When we recieve a datagram, we really don't know in advance where it's coming from, so whatever function we use for recieving will need extra parameters to provide us with the host+port from whence the datagram came. The two system calls we'll discuss for sending/recieving datagrams are:
ssize_t sendto(int s,  const  void  *msg,  size_t  len,  int flags, const struct sockaddr *to, int  tolen);

ssize_t recvfrom(int s, void *buf, size_t len, int flags, struct sockaddr *from, socklen_t *fromlen);

A simple UDP server
Just as in the TCP server, we must bind the UDP server's socket to a known port number ... otherwise clients wouldn't know where to find the server. Unlike TCP, however, there are no calls to listen or accept. Those two system calls are involved with creating connections, and so they don't apply. Instead, we simply call recvform to read the next UDP datagram that comes our way --- no matter when and from whom it arrives. Our goal this class is to develop the server that our clients from last class connected to. The process is simple:
int main(int argc, char **argv)
{
  /* Set up socket */

  /* Get current host's IP address. Use getenv(HOST) to get hostname.*/

  /* Set up address structure (use port 10000) */

  /* Bind socket to address */

  while(1)
  {
    /* Define buffer in which to recieve client's message. */

    /* Set up structure for client's host+port and recieve datagram! */

    /* Print hostname of client and message received. */
    
  }

  fprintf(stderr,"Server shutting down!\n");
  close(sfd);
  return 0;
}

As we fill this up there are few important points:
  1. Remember we're using UDP!
  2. In the call to recvfrom, the final argument is a pointer to an int: the int that it's pointing to must contain the size of the actual struct sockaddr_in you're passing vecvfrom.
  3. The recvfrom call should fill in the sockaddr_in struct with the host IP address and port number from which the datagram was received. Use the function gethostbyaddr to get the symbolic name from a pointer to an IP address.
  4. Get your server working (or copy the code below) and get two terminals open on the same host. In one, start the UDP server. In the other give the command netstat -a. Look through the netstat -a output and indentify the line corresponding to this server!

A full solution to the simple UDP server
/***********************************************************
 * A simple UDP server program.  It just reads messages and
 * prints them out along with the identity of sending host.
 ***********************************************************/
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

int main(int argc, char **argv)
{
  /* Set up socket */
  int sfd = socket(AF_INET,SOCK_DGRAM,0);
  if (sfd == -1) { fprintf(stderr,"Socket not created!\n"); exit(1); }

  /* Get current host's IP address. */
  struct hostent *p;
  p = gethostbyname(getenv("HOST"));
  if (p == NULL) { fprintf(stderr,"Name not found!\n"); exit(2); }
  unsigned int *ip = (unsigned int*)(p->h_addr_list[0]);

  /* Set up address structure */
  struct sockaddr_in mysa;
  mysa.sin_family = AF_INET;
  mysa.sin_addr.s_addr = *ip;
  mysa.sin_port = htons(10000);

  /* Bind socket to address */
  if (bind(sfd,(struct sockaddr*)&mysa,sizeof(mysa)) < 0)
  {
    close(sfd);
    fprintf(stderr,"bind failed!\n");
    exit(3);
  }

  while(1)
  {
    /* Define buffer in which to recieve client's message. */
    char msg[257] = {'\0'};

    /* Set up structure for client's host+port and recieve datagram! */
    struct sockaddr_in sourcesa = {0};
    int ssize = sizeof(sourcesa);
    int r = recvfrom(sfd,msg,256,0,(struct sockaddr*)&sourcesa,&ssize);

    /* Print host and message. */
    struct hostent *he= gethostbyaddr((char *)&sourcesa.sin_addr.s_addr,4,AF_INET);
    printf("Got message from '%s' (IP %u)!\n",he->h_name,sourcesa.sin_addr.s_addr);
    if (r == 0)
      printf("-->%i-byte message!\n",r);
    else
      printf("-->%i-byte message: %s",r,msg);
  }

  fprintf(stderr,"Server shutting down!\n");
  close(sfd);
  return 0;
}