UDP Chat Example

This small utility is an example of using UDP with poll() to create an extremely basic peer-to-peer chat application.

udpchat.c
#include <sys/types.h>
#include <sys/socket.h>
 
#include <arpa/inet.h>
#include <errno.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
 
 
void start_chat(int sock_fd, struct sockaddr_in *peer)
{
  int ret;
  ssize_t bytes;
  char input_buffer[1024];
  char output_buffer[1024];
  struct pollfd fds[2];
 
  /* Descriptor zero is stdin */
  fds[0].fd = 0;
  fds[1].fd = sock_fd;
  fds[0].events = POLLIN | POLLPRI;
  fds[1].events = POLLIN | POLLPRI;
 
  /* Normally we'd check an exit condition, but for this example
   * we loop endlessly.
   */
  while (1) {
    /* Call poll() */
    ret = poll(fds, 2, -1);
 
    if (ret < 0) {
      printf("Error - poll returned error: %s\n", strerror(errno));
      break;
    }
    if (ret > 0) {
 
      /* Regardless of requested events, poll() can always return these */
      if (fds[0].revents & (POLLERR | POLLHUP | POLLNVAL)) {
        printf("Error - poll indicated stdin error\n");
        break;
      }
      if (fds[1].revents & (POLLERR | POLLHUP | POLLNVAL)) {
        printf("Error - poll indicated socket error\n");
        break;
      }
 
      /* Check if data to read from stdin */
      if (fds[0].revents & (POLLIN | POLLPRI)) {
        bytes = read(0, output_buffer, sizeof(output_buffer));
        if (bytes < 0) {
          printf("Error - stdin error: %s\n", strerror(errno));
          break;
        }
        printf("Sending: %.*s\n", (int)bytes, output_buffer);
        bytes = sendto(sock_fd, output_buffer, bytes, 0,
                       (struct sockaddr *)peer, sizeof(struct sockaddr_in));
        if (bytes < 0) {
          printf("Error - sendto error: %s\n", strerror(errno));
          break;
        }
      }
 
      /* Check if data to read from socket */
      if (fds[1].revents & (POLLIN | POLLPRI)) {
        bytes = recvfrom(sock_fd, input_buffer, sizeof(input_buffer),
                         0, NULL, NULL);
        if (bytes < 0) {
          printf("Error - recvfrom error: %s\n", strerror(errno));
          break;
        }
        if (bytes > 0) {
          printf("Received: %.*s\n", (int)bytes, input_buffer);
        }
      }
    }
  }
}
 
 
int main(int argc, char *argv[])
{
  unsigned long local_port;
  unsigned long remote_port;
  int sock_fd;
  struct sockaddr_in server_addr;
  struct sockaddr_in peer_addr;
 
  /* Parse command line arguments for port numbers */
  if (argc < 4) {
    printf("Usage: %s <local port> <remote host> <remote port>\n", argv[0]);
    return 1;
  }
  local_port = strtoul(argv[1], NULL, 0);
  if (local_port < 1 || local_port > 65535) {
    printf("Error - invalid local port '%s'\n", argv[1]);
    return 1;
  }
  remote_port = strtoul(argv[3], NULL, 0);
  if (remote_port < 1 || remote_port > 65535) {
    printf("Error - invalid remote port '%s'\n", argv[3]);
    return 1;
  }
 
  /* Parse command line argument for remote host address */
  peer_addr.sin_family = AF_INET;
  peer_addr.sin_port = htons(remote_port);
  if (inet_aton(argv[2], &peer_addr.sin_addr) == 0) {
    printf("Error - invalid remote address '%s'\n", argv[2]);
    return 1;
  }
 
  /* Create UDP socket */
  sock_fd = socket(AF_INET, SOCK_DGRAM, 0);
  if (sock_fd < 0) {
    printf("Error - failed to open socket: %s\n", strerror(errno));
    return 1;
  }
 
  /* Bind socket */
  server_addr.sin_family = AF_INET;
  server_addr.sin_addr.s_addr = htonl(INADDR_ANY);
  server_addr.sin_port = htons(local_port);
  if (bind(sock_fd, (struct sockaddr *)(&server_addr),
           sizeof(server_addr)) < 0) {
    printf("Error - failed to bind socket: %s\n", strerror(errno));
    return 1;
  }
 
  /* Call chat handler to loop */
  start_chat(sock_fd, &peer_addr);
 
  close(sock_fd);
 
  return 0;
}